@heymantle/litho 0.0.10 → 0.0.12

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 (91) hide show
  1. package/dist/cjs/components/Banner.js +6 -1
  2. package/dist/cjs/components/Button.js +33 -4
  3. package/dist/cjs/components/ButtonGroup.js +19 -4
  4. package/dist/cjs/components/Card.js +39 -3
  5. package/dist/cjs/components/ChoiceList.js +3 -2
  6. package/dist/cjs/components/Code.js +227 -0
  7. package/dist/cjs/components/Filters.js +1 -1
  8. package/dist/cjs/components/Frame.js +2 -2
  9. package/dist/cjs/components/Layout.js +16 -4
  10. package/dist/cjs/components/Link.js +35 -4
  11. package/dist/cjs/components/Modal.js +4 -0
  12. package/dist/cjs/components/Page.js +5 -2
  13. package/dist/cjs/components/Pane.js +669 -84
  14. package/dist/cjs/components/ResourceList.js +2 -2
  15. package/dist/cjs/components/TabNavigation.js +300 -0
  16. package/dist/cjs/components/TextField.js +3 -0
  17. package/dist/cjs/components/Tip.js +3 -0
  18. package/dist/cjs/components/Tooltip.js +12 -13
  19. package/dist/cjs/index.js +4 -0
  20. package/dist/cjs/stories/Pane.stories.js +352 -3
  21. package/dist/cjs/utilities/useBodyScrollLock.js +63 -0
  22. package/dist/cjs/utilities/useKeyboardAction.js +19 -0
  23. package/dist/cjs/utilities/useLocalStorage.js +126 -0
  24. package/dist/cjs/utilities/useMobile.js +92 -0
  25. package/dist/cjs/utilities/usePaneState.js +340 -0
  26. package/dist/cjs/utilities/useTabStorage.js +325 -0
  27. package/dist/esm/components/Banner.js +7 -2
  28. package/dist/esm/components/Button.js +33 -4
  29. package/dist/esm/components/ButtonGroup.js +19 -4
  30. package/dist/esm/components/Card.js +39 -3
  31. package/dist/esm/components/ChoiceList.js +3 -2
  32. package/dist/esm/components/Code.js +212 -0
  33. package/dist/esm/components/Filters.js +2 -2
  34. package/dist/esm/components/Frame.js +2 -2
  35. package/dist/esm/components/Layout.js +16 -4
  36. package/dist/esm/components/Link.js +31 -5
  37. package/dist/esm/components/Modal.js +4 -0
  38. package/dist/esm/components/Page.js +5 -2
  39. package/dist/esm/components/Pane.js +619 -83
  40. package/dist/esm/components/ResourceList.js +2 -2
  41. package/dist/esm/components/TabNavigation.js +285 -0
  42. package/dist/esm/components/TextField.js +4 -1
  43. package/dist/esm/components/Tip.js +4 -1
  44. package/dist/esm/components/Tooltip.js +12 -13
  45. package/dist/esm/index.js +1 -0
  46. package/dist/esm/stories/Pane.stories.js +346 -3
  47. package/dist/esm/utilities/useBodyScrollLock.js +53 -0
  48. package/dist/esm/utilities/useKeyboardAction.js +25 -0
  49. package/dist/esm/utilities/useLocalStorage.js +115 -0
  50. package/dist/esm/utilities/useMobile.js +79 -0
  51. package/dist/esm/utilities/usePaneState.js +334 -0
  52. package/dist/esm/utilities/useTabStorage.js +311 -0
  53. package/dist/types/components/Banner.d.ts.map +1 -1
  54. package/dist/types/components/Button.d.ts +2 -2
  55. package/dist/types/components/Button.d.ts.map +1 -1
  56. package/dist/types/components/ButtonGroup.d.ts.map +1 -1
  57. package/dist/types/components/Card.d.ts +34 -1
  58. package/dist/types/components/Card.d.ts.map +1 -1
  59. package/dist/types/components/Code.d.ts +28 -0
  60. package/dist/types/components/Code.d.ts.map +1 -0
  61. package/dist/types/components/Filters.d.ts.map +1 -1
  62. package/dist/types/components/Layout.d.ts +2 -0
  63. package/dist/types/components/Layout.d.ts.map +1 -1
  64. package/dist/types/components/Link.d.ts +4 -0
  65. package/dist/types/components/Link.d.ts.map +1 -1
  66. package/dist/types/components/Modal.d.ts +2 -0
  67. package/dist/types/components/Modal.d.ts.map +1 -1
  68. package/dist/types/components/Page.d.ts.map +1 -1
  69. package/dist/types/components/Pane.d.ts +2 -0
  70. package/dist/types/components/Pane.d.ts.map +1 -1
  71. package/dist/types/components/TabNavigation.d.ts +3 -0
  72. package/dist/types/components/TabNavigation.d.ts.map +1 -0
  73. package/dist/types/components/TextField.d.ts.map +1 -1
  74. package/dist/types/components/Tip.d.ts.map +1 -1
  75. package/dist/types/components/Tooltip.d.ts +2 -0
  76. package/dist/types/components/Tooltip.d.ts.map +1 -1
  77. package/dist/types/index.d.ts +1 -0
  78. package/dist/types/utilities/useBodyScrollLock.d.ts +12 -0
  79. package/dist/types/utilities/useBodyScrollLock.d.ts.map +1 -0
  80. package/dist/types/utilities/useKeyboardAction.d.ts +2 -0
  81. package/dist/types/utilities/useKeyboardAction.d.ts.map +1 -0
  82. package/dist/types/utilities/useLocalStorage.d.ts +13 -0
  83. package/dist/types/utilities/useLocalStorage.d.ts.map +1 -0
  84. package/dist/types/utilities/useMobile.d.ts +9 -0
  85. package/dist/types/utilities/useMobile.d.ts.map +1 -0
  86. package/dist/types/utilities/usePaneState.d.ts +2 -0
  87. package/dist/types/utilities/usePaneState.d.ts.map +1 -0
  88. package/dist/types/utilities/useTabStorage.d.ts +8 -0
  89. package/dist/types/utilities/useTabStorage.d.ts.map +1 -0
  90. package/index.css +77 -6
  91. package/package.json +1 -1
@@ -3,21 +3,36 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- Object.defineProperty(exports, "default", {
7
- enumerable: true,
8
- get: function() {
6
+ function _export(target, all) {
7
+ for(var name in all)Object.defineProperty(target, name, {
8
+ enumerable: true,
9
+ get: Object.getOwnPropertyDescriptor(all, name).get
10
+ });
11
+ }
12
+ _export(exports, {
13
+ get PaneContext () {
14
+ return PaneContext;
15
+ },
16
+ get default () {
9
17
  return _default;
10
18
  }
11
19
  });
12
20
  var _jsxruntime = require("react/jsx-runtime");
13
21
  var _polarisicons = require("@shopify/polaris-icons");
14
22
  var _tailwindvariants = require("tailwind-variants");
15
- var _react = require("react");
23
+ var _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
16
24
  var _Text = /*#__PURE__*/ _interop_require_default(require("./Text"));
17
25
  var _Button = /*#__PURE__*/ _interop_require_default(require("./Button"));
18
26
  var _Popover = /*#__PURE__*/ _interop_require_default(require("./Popover"));
19
27
  var _ActionList = /*#__PURE__*/ _interop_require_default(require("./ActionList"));
28
+ var _TabNavigation = /*#__PURE__*/ _interop_require_default(require("./TabNavigation"));
20
29
  var _Frame = require("./Frame");
30
+ var _Icon = /*#__PURE__*/ _interop_require_default(require("./Icon"));
31
+ var _usePaneState = require("../utilities/usePaneState");
32
+ var _useMobile = require("../utilities/useMobile");
33
+ var _useBodyScrollLock = require("../utilities/useBodyScrollLock");
34
+ var _useKeyboardAction = require("../utilities/useKeyboardAction");
35
+ var _Tooltip = /*#__PURE__*/ _interop_require_default(require("./Tooltip"));
21
36
  function _array_like_to_array(arr, len) {
22
37
  if (len == null || len > arr.length) len = arr.length;
23
38
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
@@ -31,6 +46,47 @@ function _interop_require_default(obj) {
31
46
  default: obj
32
47
  };
33
48
  }
49
+ function _getRequireWildcardCache(nodeInterop) {
50
+ if (typeof WeakMap !== "function") return null;
51
+ var cacheBabelInterop = new WeakMap();
52
+ var cacheNodeInterop = new WeakMap();
53
+ return (_getRequireWildcardCache = function(nodeInterop) {
54
+ return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
55
+ })(nodeInterop);
56
+ }
57
+ function _interop_require_wildcard(obj, nodeInterop) {
58
+ if (!nodeInterop && obj && obj.__esModule) {
59
+ return obj;
60
+ }
61
+ if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
62
+ return {
63
+ default: obj
64
+ };
65
+ }
66
+ var cache = _getRequireWildcardCache(nodeInterop);
67
+ if (cache && cache.has(obj)) {
68
+ return cache.get(obj);
69
+ }
70
+ var newObj = {
71
+ __proto__: null
72
+ };
73
+ var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
74
+ for(var key in obj){
75
+ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
76
+ var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
77
+ if (desc && (desc.get || desc.set)) {
78
+ Object.defineProperty(newObj, key, desc);
79
+ } else {
80
+ newObj[key] = obj[key];
81
+ }
82
+ }
83
+ }
84
+ newObj.default = obj;
85
+ if (cache) {
86
+ cache.set(obj, newObj);
87
+ }
88
+ return newObj;
89
+ }
34
90
  function _iterable_to_array_limit(arr, i) {
35
91
  var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
36
92
  if (_i == null) return;
@@ -69,27 +125,489 @@ function _unsupported_iterable_to_array(o, minLen) {
69
125
  if (n === "Map" || n === "Set") return Array.from(n);
70
126
  if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
71
127
  }
128
+ // Reference counter for pane open state (shared across all Pane components)
129
+ var paneOpenCount = 0;
130
+ var MIN_PANE_WIDTH = 360;
131
+ var MAX_PANE_WIDTH = 800;
132
+ var COLLAPSED_WIDTH = 28;
133
+ var MOBILE_BREAKPOINT = 768;
134
+ var ZERO_WIDTH = '0px';
135
+ var COLLAPSED_WIDTH_STR = "".concat(COLLAPSED_WIDTH, "px");
136
+ var calculatePaneWidth = function(open, isMobile, isCollapsed, paneWidth) {
137
+ // Early returns for common cases (ordered by frequency/likelihood)
138
+ if (!open) return ZERO_WIDTH;
139
+ if (isCollapsed) return COLLAPSED_WIDTH_STR;
140
+ if (isMobile) return ZERO_WIDTH; // Full screen on mobile when expanded
141
+ // Only format paneWidth when actually needed
142
+ return "".concat(paneWidth, "px");
143
+ };
72
144
  var styles = (0, _tailwindvariants.tv)({
73
- base: "Litho-Pane hidden md:block fixed inset-0 w-full md:left-auto md:max-w-[var(--litho-pane-width)] block bg-surface-lowest shadow-pane-inner pointer-events-auto"
145
+ base: "Litho-Pane fixed right-0 w-full md:w-[var(--litho-pane-width)] bg-surface-lowest shadow-pane-inner pointer-events-auto transition-all duration-200",
146
+ variants: {
147
+ collapsed: {
148
+ true: "md:w-7 md:h-screen md:flex-shrink-0"
149
+ },
150
+ isMobile: {
151
+ true: "top-0 bottom-0",
152
+ false: "top-0 h-full"
153
+ }
154
+ },
155
+ defaultVariants: {
156
+ isMobile: false
157
+ }
74
158
  });
75
159
  var innerStyles = (0, _tailwindvariants.tv)({
76
- base: "relative flex flex-col overflow-y-auto h-full"
160
+ base: "relative flex flex-col h-full",
161
+ variants: {
162
+ collapsed: {
163
+ true: "bg-surface-lowest cursor-pointer overflow-visible",
164
+ false: "overflow-hidden"
165
+ }
166
+ }
167
+ });
168
+ // Content styles will be applied inline with dynamic width
169
+ var getContentStyles = function(collapsed, width) {
170
+ return {
171
+ width: width,
172
+ transition: 'opacity 200ms ease-in-out',
173
+ pointerEvents: collapsed ? 'none' : 'auto',
174
+ opacity: collapsed ? 0 : 1
175
+ };
176
+ };
177
+ var resizeHandleStyles = (0, _tailwindvariants.tv)({
178
+ base: "absolute top-0 left-0 w-1 h-full transition-colors duration-300 hover-delay hover:bg-tint-3 cursor-col-resize z-10",
179
+ variants: {
180
+ resizing: {
181
+ true: "bg-tint-3"
182
+ }
183
+ }
77
184
  });
78
- var dragHandleStyles = (0, _tailwindvariants.tv)({
79
- base: "cursor-col-resize absolute top-0 left-0 w-1 h-full transition-colors duration-300 hover-delay hover:bg-tint-3"
185
+ var collapseHandleStyles = (0, _tailwindvariants.tv)({
186
+ base: "group/collapse-handle bg-surface-normal w-5 hover:w-6 h-10 flex-shrink-0 flex items-center justify-center cursor-pointer transition-all duration-200 ease-in-out rounded-r-sm",
187
+ variants: {
188
+ variant: {
189
+ expanded: "",
190
+ collapsed: "hover:bg-surface-lower"
191
+ }
192
+ },
193
+ defaultVariants: {
194
+ variant: "expanded"
195
+ }
80
196
  });
81
197
  var PaneContext = /*#__PURE__*/ (0, _react.createContext)({});
198
+ // Collapse button component to reduce duplication
199
+ function CollapseButton(param) {
200
+ var variant = param.variant, icon = param.icon, label = param.label, onClick = param.onClick, onKeyDown = param.onKeyDown, _param_stopPropagation = param.stopPropagation, stopPropagation = _param_stopPropagation === void 0 ? false : _param_stopPropagation;
201
+ return /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
202
+ className: "absolute left-0 top-1/2 -translate-y-1/2",
203
+ children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_Tooltip.default, {
204
+ content: label,
205
+ preferredPosition: "above",
206
+ alignment: "right",
207
+ children: /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
208
+ className: collapseHandleStyles({
209
+ variant: variant
210
+ }),
211
+ onClick: function(e) {
212
+ if (stopPropagation) e.stopPropagation();
213
+ onClick(e);
214
+ },
215
+ role: "button",
216
+ tabIndex: 0,
217
+ onKeyDown: onKeyDown,
218
+ "aria-label": label,
219
+ children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_Icon.default, {
220
+ source: icon,
221
+ color: "subdued"
222
+ })
223
+ })
224
+ })
225
+ });
226
+ }
82
227
  function Pane() {
83
228
  var props = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
84
- var open = props.open, onClose = props.onClose, children = props.children;
229
+ var _tabStorage_tabs;
230
+ var open = props.open, onClose = props.onClose, onOpen = props.onOpen, children = props.children, tabStorage = props.tabStorage, onTabRemove = props.onTabRemove, onExpandRef = props.onExpandRef, // Generic tab configuration
231
+ tabId = props.tabId, tabTitle = props.tabTitle, tabKey = props.tabKey, orgId = props.orgId;
85
232
  var setPaneIsOpen = (0, _Frame.useFrame)().setPaneIsOpen;
86
- var classes = styles();
233
+ var paneRef = (0, _react.useRef)(null);
234
+ var resizeHandleRef = (0, _react.useRef)(null);
235
+ var paneWidthRef = (0, _react.useRef)(null);
236
+ var resizeHandlersRef = (0, _react.useRef)({
237
+ mouseMove: null,
238
+ mouseUp: null
239
+ });
240
+ var isMountedRef = (0, _react.useRef)(true);
241
+ var hasRegisteredRef = (0, _react.useRef)(false);
242
+ var previousTabCountRef = (0, _react.useRef)(0);
243
+ var _useState = _sliced_to_array((0, _react.useState)(false), 2), isTransitioning = _useState[0], setIsTransitioning = _useState[1];
244
+ var isPastInitialMountRef = (0, _react.useRef)(false);
245
+ var previousOpenPropRef = (0, _react.useRef)(open);
246
+ // Get orgId from prop or tabStorage (tabStorage always has orgId set, either passed or 'default')
247
+ var currentOrgId = orgId || (tabStorage === null || tabStorage === void 0 ? void 0 : tabStorage.orgId);
248
+ // Use consolidated pane state hook with orgId
249
+ var _usePaneState1 = (0, _usePaneState.usePaneState)(currentOrgId), paneState = _usePaneState1.state, setPaneWidth = _usePaneState1.setWidth, setIsCollapsed = _usePaneState1.setCollapsed, setPaneOpen = _usePaneState1.setOpen, refreshFromStorage = _usePaneState1.refreshFromStorage;
250
+ var isCollapsed = paneState.collapsed;
251
+ var paneWidth = paneState.width;
252
+ // Helper to open the pane (updates localStorage and notifies parent)
253
+ var openPane = (0, _react.useCallback)(function() {
254
+ setPaneOpen(true);
255
+ onOpen === null || onOpen === void 0 ? void 0 : onOpen();
256
+ }, [
257
+ setPaneOpen,
258
+ onOpen
259
+ ]);
260
+ // Helper to close the pane (updates localStorage and resets collapsed state)
261
+ var closePane = (0, _react.useCallback)(function() {
262
+ setPaneOpen(false);
263
+ setIsCollapsed(false);
264
+ }, [
265
+ setPaneOpen,
266
+ setIsCollapsed
267
+ ]);
268
+ // Helper to close pane and notify parent (consolidates common pattern)
269
+ var closePaneAndNotify = (0, _react.useCallback)(function() {
270
+ closePane();
271
+ onClose === null || onClose === void 0 ? void 0 : onClose();
272
+ }, [
273
+ closePane,
274
+ onClose
275
+ ]);
276
+ // Read from localStorage when pane opens (user action, not page load)
277
+ (0, _react.useEffect)(function() {
278
+ if (open && refreshFromStorage && currentOrgId) {
279
+ refreshFromStorage();
280
+ }
281
+ }, [
282
+ open,
283
+ refreshFromStorage,
284
+ currentOrgId
285
+ ]);
286
+ // Update pane open state in localStorage when open prop changes
287
+ // The setPaneOpen setter will handle first-time state creation automatically
288
+ (0, _react.useEffect)(function() {
289
+ if (!currentOrgId) return;
290
+ // Only update if open prop actually changed (not on every render)
291
+ if (previousOpenPropRef.current === open) return;
292
+ previousOpenPropRef.current = open;
293
+ if (open) {
294
+ // Set open to true when pane opens (creates state if it doesn't exist)
295
+ setPaneOpen(true);
296
+ } else {
297
+ // Set open to false when pane closes, and reset collapsed state
298
+ closePane();
299
+ }
300
+ }, [
301
+ open,
302
+ currentOrgId,
303
+ setPaneOpen,
304
+ closePane
305
+ ]);
306
+ var _useState1 = _sliced_to_array((0, _react.useState)(false), 2), isResizing = _useState1[0], setIsResizing = _useState1[1];
307
+ // Use mobile detection hook
308
+ var isMobile = (0, _useMobile.useMobile)(MOBILE_BREAKPOINT);
309
+ // Keep ref in sync with state
310
+ (0, _react.useEffect)(function() {
311
+ paneWidthRef.current = paneWidth;
312
+ }, [
313
+ paneWidth
314
+ ]);
315
+ // Get the active tab data
316
+ var activeTab = (0, _react.useMemo)(function() {
317
+ if (!(tabStorage === null || tabStorage === void 0 ? void 0 : tabStorage.activeTabId)) return null;
318
+ return tabStorage.tabs.find(function(tab) {
319
+ return tab.id === tabStorage.activeTabId;
320
+ }) || null;
321
+ }, [
322
+ tabStorage === null || tabStorage === void 0 ? void 0 : tabStorage.tabs,
323
+ tabStorage === null || tabStorage === void 0 ? void 0 : tabStorage.activeTabId
324
+ ]);
325
+ // Handle tab creation, activation, and ensure active tab exists when pane opens
326
+ (0, _react.useEffect)(function() {
327
+ if (!open || !tabStorage) return;
328
+ // If tabId and tabTitle are provided, create/update/activate that tab
329
+ if (tabId && tabTitle) {
330
+ var existingTab = tabStorage.tabs.find(function(tab) {
331
+ return tab.id === tabId;
332
+ });
333
+ var wasAlreadyActive = existingTab && tabStorage.activeTabId === tabId;
334
+ if (existingTab) {
335
+ // Tab exists - switch to it if not already active
336
+ if (!wasAlreadyActive) {
337
+ tabStorage.setActiveTab(tabId);
338
+ }
339
+ // Update title/key if changed
340
+ if (existingTab.title !== tabTitle) {
341
+ tabStorage.updateTab(tabId, {
342
+ title: tabTitle
343
+ });
344
+ }
345
+ if (tabKey !== undefined && existingTab.key !== tabKey) {
346
+ tabStorage.updateTab(tabId, {
347
+ key: tabKey
348
+ });
349
+ }
350
+ } else {
351
+ // Create new tab
352
+ tabStorage.addTab({
353
+ id: tabId,
354
+ title: tabTitle,
355
+ key: tabKey
356
+ });
357
+ }
358
+ }
359
+ // Ensure an active tab is set (fallback if tabId/tabTitle not provided or if active tab doesn't exist)
360
+ if (tabStorage.tabs.length > 0) {
361
+ var activeTabExists = tabStorage.activeTabId && tabStorage.tabs.some(function(tab) {
362
+ return tab.id === tabStorage.activeTabId;
363
+ });
364
+ if (!activeTabExists) {
365
+ var firstTab = tabStorage.tabs[0];
366
+ if (firstTab) {
367
+ tabStorage.setActiveTab(firstTab.id);
368
+ }
369
+ }
370
+ }
371
+ }, [
372
+ open,
373
+ tabStorage,
374
+ tabId,
375
+ tabTitle,
376
+ tabKey
377
+ ]);
378
+ // Auto-open pane when tabs are added (user action, not page load)
379
+ (0, _react.useEffect)(function() {
380
+ var _tabStorage_tabs;
381
+ if (!tabStorage || !currentOrgId) {
382
+ previousTabCountRef.current = 0;
383
+ return;
384
+ }
385
+ var currentTabCount = ((_tabStorage_tabs = tabStorage.tabs) === null || _tabStorage_tabs === void 0 ? void 0 : _tabStorage_tabs.length) || 0;
386
+ var previousTabCount = previousTabCountRef.current;
387
+ // Skip on initial mount - only react to tab count changes after mount
388
+ if (!isPastInitialMountRef.current) {
389
+ previousTabCountRef.current = currentTabCount;
390
+ isPastInitialMountRef.current = true;
391
+ return;
392
+ }
393
+ // If tab count increased (user added a tab) and pane is closed, open it
394
+ if (currentTabCount > previousTabCount && !open) {
395
+ openPane();
396
+ }
397
+ previousTabCountRef.current = currentTabCount;
398
+ }, [
399
+ tabStorage === null || tabStorage === void 0 ? void 0 : (_tabStorage_tabs = tabStorage.tabs) === null || _tabStorage_tabs === void 0 ? void 0 : _tabStorage_tabs.length,
400
+ currentOrgId,
401
+ open,
402
+ openPane
403
+ ]);
404
+ // Update frame context when pane opens/closes
405
+ // Uses reference counting to coordinate between multiple Pane components
406
+ (0, _react.useEffect)(function() {
407
+ if (open && !hasRegisteredRef.current) {
408
+ paneOpenCount++;
409
+ if (paneOpenCount === 1) {
410
+ setPaneIsOpen(true);
411
+ }
412
+ hasRegisteredRef.current = true;
413
+ } else if (!open && hasRegisteredRef.current) {
414
+ paneOpenCount = Math.max(0, paneOpenCount - 1);
415
+ if (paneOpenCount === 0) {
416
+ setPaneIsOpen(false);
417
+ }
418
+ hasRegisteredRef.current = false;
419
+ }
420
+ return function() {
421
+ if (hasRegisteredRef.current) {
422
+ paneOpenCount = Math.max(0, paneOpenCount - 1);
423
+ if (paneOpenCount === 0) {
424
+ setPaneIsOpen(false);
425
+ }
426
+ hasRegisteredRef.current = false;
427
+ }
428
+ };
429
+ }, [
430
+ open,
431
+ setPaneIsOpen
432
+ ]);
433
+ // Uses shared utility with reference counting to coordinate with other components
434
+ (0, _useBodyScrollLock.useBodyScrollLock)(open && isMobile && !isCollapsed);
435
+ // Memoize calculated width to avoid unnecessary recalculations
436
+ var calculatedWidth = (0, _react.useMemo)(function() {
437
+ return calculatePaneWidth(open, isMobile, isCollapsed, paneWidth);
438
+ }, [
439
+ open,
440
+ isMobile,
441
+ isCollapsed,
442
+ paneWidth
443
+ ]);
444
+ // Calculate content width - keep width at paneWidth when collapsed so translateX works
445
+ var contentWidth = (0, _react.useMemo)(function() {
446
+ if (!open) return '0px';
447
+ // On mobile, use full width when expanded; on desktop, use paneWidth
448
+ // Keep width at paneWidth even when collapsed so translateX(100%) can slide it off-screen
449
+ if (isMobile) return '100%';
450
+ return "".concat(paneWidth, "px");
451
+ }, [
452
+ open,
453
+ isMobile,
454
+ paneWidth
455
+ ]);
456
+ // Update CSS variable for pane width
457
+ (0, _react.useEffect)(function() {
458
+ document.documentElement.style.setProperty('--litho-pane-width', calculatedWidth);
459
+ }, [
460
+ calculatedWidth
461
+ ]);
462
+ // Cleanup resize handlers on unmount
463
+ (0, _react.useEffect)(function() {
464
+ isMountedRef.current = true;
465
+ return function() {
466
+ isMountedRef.current = false;
467
+ // Clean up any active resize handlers if component unmounts during drag
468
+ var _resizeHandlersRef_current = resizeHandlersRef.current, mouseMove = _resizeHandlersRef_current.mouseMove, mouseUp = _resizeHandlersRef_current.mouseUp;
469
+ if (mouseMove) {
470
+ document.removeEventListener('mousemove', mouseMove);
471
+ }
472
+ if (mouseUp) {
473
+ document.removeEventListener('mouseup', mouseUp);
474
+ }
475
+ resizeHandlersRef.current = {
476
+ mouseMove: null,
477
+ mouseUp: null
478
+ };
479
+ };
480
+ }, []);
481
+ // Handle resize drag with improved performance
482
+ // Updates CSS variable directly during drag for smooth performance
483
+ var handleResizeStart = (0, _react.useCallback)(function(e) {
484
+ e.preventDefault();
485
+ if (!isMountedRef.current) return;
486
+ setIsResizing(true);
487
+ var startX = e.clientX;
488
+ // Use ref to get current width to avoid stale closure
489
+ var startWidth = paneWidthRef.current || paneWidth;
490
+ var currentWidth = startWidth;
491
+ // Disable transitions during drag for immediate response
492
+ var paneElement = paneRef.current;
493
+ var originalTransition = paneElement === null || paneElement === void 0 ? void 0 : paneElement.style.transition;
494
+ if (paneElement) {
495
+ paneElement.style.transition = 'none';
496
+ }
497
+ var handleMouseMove = function(e) {
498
+ // Check if component is still mounted before updating
499
+ if (!isMountedRef.current) return;
500
+ // Calculate new width directly without RAF for immediate response
501
+ var diff = startX - e.clientX; // Inverted because we're dragging from the left edge
502
+ var newWidth = Math.max(MIN_PANE_WIDTH, Math.min(MAX_PANE_WIDTH, startWidth + diff));
503
+ currentWidth = newWidth;
504
+ // Update CSS variable directly for immediate visual feedback (no re-render)
505
+ document.documentElement.style.setProperty('--litho-pane-width', "".concat(newWidth, "px"));
506
+ };
507
+ var handleMouseUp = function() {
508
+ // Check if component is still mounted before updating state
509
+ if (!isMountedRef.current) {
510
+ // Clean up listeners even if unmounted
511
+ document.removeEventListener('mousemove', handleMouseMove);
512
+ document.removeEventListener('mouseup', handleMouseUp);
513
+ resizeHandlersRef.current = {
514
+ mouseMove: null,
515
+ mouseUp: null
516
+ };
517
+ return;
518
+ }
519
+ // Restore transitions
520
+ if (paneElement) {
521
+ paneElement.style.transition = originalTransition || '';
522
+ }
523
+ // Sync final width to state (triggers re-render and localStorage save)
524
+ setPaneWidth(currentWidth);
525
+ setIsResizing(false);
526
+ document.removeEventListener('mousemove', handleMouseMove);
527
+ document.removeEventListener('mouseup', handleMouseUp);
528
+ resizeHandlersRef.current = {
529
+ mouseMove: null,
530
+ mouseUp: null
531
+ };
532
+ };
533
+ // Store handlers in ref for cleanup
534
+ resizeHandlersRef.current = {
535
+ mouseMove: handleMouseMove,
536
+ mouseUp: handleMouseUp
537
+ };
538
+ document.addEventListener('mousemove', handleMouseMove);
539
+ document.addEventListener('mouseup', handleMouseUp);
540
+ }, [
541
+ paneWidth,
542
+ setPaneWidth,
543
+ setIsResizing
544
+ ]);
545
+ var toggleCollapsed = (0, _react.useCallback)(function() {
546
+ setIsCollapsed(function(prev) {
547
+ var newValue = !prev;
548
+ // Track transition start to prevent hover flash
549
+ setIsTransitioning(true);
550
+ // Clear transition flag after animation completes
551
+ setTimeout(function() {
552
+ setIsTransitioning(false);
553
+ }, 200); // Match transition duration
554
+ return newValue;
555
+ });
556
+ }, [
557
+ setIsCollapsed
558
+ ]);
559
+ // Expand pane function - exposed to parent via ref and tabStorage
560
+ var expandPane = (0, _react.useCallback)(function() {
561
+ setIsCollapsed(false);
562
+ }, [
563
+ setIsCollapsed
564
+ ]);
565
+ // Expose expand function to parent via ref and tabStorage
566
+ (0, _react.useEffect)(function() {
567
+ // Expose to parent ref if provided
568
+ if (onExpandRef) {
569
+ onExpandRef.current = expandPane;
570
+ }
571
+ // Expose to tabStorage for hooks to use
572
+ if (tabStorage) {
573
+ if (!tabStorage.expandPaneRef) {
574
+ tabStorage.expandPaneRef = {
575
+ current: null
576
+ };
577
+ }
578
+ tabStorage.expandPaneRef.current = expandPane;
579
+ }
580
+ // Cleanup: clear refs when component unmounts or refs change
581
+ return function() {
582
+ if (onExpandRef) {
583
+ onExpandRef.current = null;
584
+ }
585
+ if (tabStorage === null || tabStorage === void 0 ? void 0 : tabStorage.expandPaneRef) {
586
+ tabStorage.expandPaneRef.current = null;
587
+ }
588
+ };
589
+ }, [
590
+ onExpandRef,
591
+ tabStorage,
592
+ expandPane
593
+ ]);
594
+ // Expose isCollapsed synchronously using useLayoutEffect (runs before paint)
595
+ (0, _react.useLayoutEffect)(function() {
596
+ if (tabStorage) {
597
+ tabStorage.isCollapsed = isCollapsed;
598
+ }
599
+ }, [
600
+ tabStorage,
601
+ isCollapsed
602
+ ]);
603
+ // Handle keyboard interactions
604
+ var handleKeyboardAction = (0, _useKeyboardAction.useKeyboardAction)();
605
+ // Handle Escape key to close
87
606
  (0, _react.useEffect)(function() {
88
- setPaneIsOpen(open);
89
607
  if (!open || !onClose) return;
90
608
  var handleKeyDown = function(event) {
91
609
  if (event.key === "Escape") {
92
- onClose();
610
+ closePaneAndNotify();
93
611
  }
94
612
  };
95
613
  document.addEventListener("keydown", handleKeyDown);
@@ -98,36 +616,121 @@ function Pane() {
98
616
  };
99
617
  }, [
100
618
  open,
101
- onClose
619
+ onClose,
620
+ closePaneAndNotify
621
+ ]);
622
+ var handleTabClick = (0, _react.useCallback)(function(tabId) {
623
+ if (!tabStorage) return;
624
+ tabStorage.setActiveTab(tabId);
625
+ // Don't expand when clicking tabs - collapsed state should not be changed
626
+ }, [
627
+ tabStorage
102
628
  ]);
629
+ var handleTabClose = (0, _react.useCallback)(function(tabId) {
630
+ if (!tabStorage) return;
631
+ var tabToRemove = tabStorage.tabs.find(function(tab) {
632
+ return tab.id === tabId;
633
+ });
634
+ var remainingTabsCount = tabStorage.tabs.length - 1;
635
+ // Remove the tab first
636
+ tabStorage.removeTab(tabId);
637
+ // Only close the pane if this was the last tab
638
+ if (remainingTabsCount === 0 && open && onClose) {
639
+ closePaneAndNotify();
640
+ }
641
+ // Notify parent about the removed tab
642
+ if (onTabRemove) {
643
+ onTabRemove(tabId, tabToRemove);
644
+ }
645
+ }, [
646
+ tabStorage,
647
+ open,
648
+ onClose,
649
+ onTabRemove,
650
+ closePane
651
+ ]);
652
+ // Memoize style object to avoid recreating on every render
653
+ // Must be called before early return to maintain hook order
654
+ var paneStyle = (0, _react.useMemo)(function() {
655
+ return isCollapsed ? {
656
+ width: "".concat(COLLAPSED_WIDTH, "px")
657
+ } : undefined;
658
+ }, [
659
+ isCollapsed
660
+ ]);
661
+ var classes = styles({
662
+ collapsed: isCollapsed,
663
+ isMobile: isMobile
664
+ });
665
+ var innerClasses = innerStyles({
666
+ collapsed: isCollapsed
667
+ });
103
668
  if (!open) {
104
669
  return null;
105
670
  }
106
671
  return /*#__PURE__*/ (0, _jsxruntime.jsx)(PaneContext.Provider, {
107
672
  value: {
108
- onClose: onClose
673
+ activeTab: activeTab,
674
+ onClose: onClose,
675
+ isCollapsed: isCollapsed,
676
+ toggleCollapsed: toggleCollapsed
109
677
  },
110
678
  children: /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
679
+ ref: paneRef,
111
680
  className: classes,
112
- children: /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
113
- className: innerStyles(),
114
- children: children
681
+ style: paneStyle,
682
+ children: /*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
683
+ className: "".concat(innerClasses, " ").concat(isCollapsed && !isTransitioning ? 'hover:bg-[#D5D3D1]' : ''),
684
+ onClick: isCollapsed ? toggleCollapsed : undefined,
685
+ children: [
686
+ /*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
687
+ className: "flex flex-col overflow-y-auto overflow-x-hidden h-full",
688
+ style: getContentStyles(isCollapsed, contentWidth),
689
+ children: [
690
+ tabStorage && tabStorage.tabs.length > 0 && /*#__PURE__*/ (0, _jsxruntime.jsx)(_TabNavigation.default, {
691
+ tabs: tabStorage.tabs,
692
+ activeTabId: tabStorage.activeTabId,
693
+ onTabClick: handleTabClick,
694
+ onTabClose: handleTabClose
695
+ }),
696
+ children,
697
+ /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
698
+ ref: resizeHandleRef,
699
+ className: resizeHandleStyles({
700
+ resizing: isResizing
701
+ }),
702
+ onMouseDown: handleResizeStart
703
+ }),
704
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(CollapseButton, {
705
+ variant: "expanded",
706
+ icon: _polarisicons.ChevronRightMinor,
707
+ label: "Collapse pane",
708
+ onClick: toggleCollapsed,
709
+ onKeyDown: function(e) {
710
+ return handleKeyboardAction(e, toggleCollapsed);
711
+ }
712
+ })
713
+ ]
714
+ }),
715
+ isCollapsed && /*#__PURE__*/ (0, _jsxruntime.jsx)(CollapseButton, {
716
+ variant: "collapsed",
717
+ icon: _polarisicons.ChevronLeftMinor,
718
+ label: "Expand pane",
719
+ onClick: toggleCollapsed,
720
+ onKeyDown: function(e) {
721
+ return handleKeyboardAction(e, toggleCollapsed);
722
+ },
723
+ stopPropagation: true
724
+ })
725
+ ]
115
726
  })
116
727
  })
117
728
  });
118
729
  }
119
730
  function Header() {
120
731
  var props = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
121
- var title = props.title, subtitle = props.subtitle, _props_actions = props.actions, actions = _props_actions === void 0 ? [] : _props_actions, _props_showCloseButton = props.showCloseButton, showCloseButton = _props_showCloseButton === void 0 ? true : _props_showCloseButton, _props_bottomBorder = props.bottomBorder, bottomBorder = _props_bottomBorder === void 0 ? true : _props_bottomBorder, subHeader = props.subHeader, tmp = props.backAction, _backAction = tmp === void 0 ? null : tmp, _props_expanded = props.expanded, expanded = _props_expanded === void 0 ? false : _props_expanded, _props_onToggleExpand = props.onToggleExpand, onToggleExpand = _props_onToggleExpand === void 0 ? function() {} : _props_onToggleExpand, _props_allowExpand = props.allowExpand, allowExpand = _props_allowExpand === void 0 ? true : _props_allowExpand;
122
- var onClose = (0, _react.useContext)(PaneContext).onClose;
123
- var showActionsOrClose = actions.length > 0 || showCloseButton;
732
+ var title = props.title, subtitle = props.subtitle, _props_actions = props.actions, actions = _props_actions === void 0 ? [] : _props_actions, _props_bottomBorder = props.bottomBorder, bottomBorder = _props_bottomBorder === void 0 ? true : _props_bottomBorder, subHeader = props.subHeader, tmp = props.backAction, _backAction = tmp === void 0 ? null : tmp;
124
733
  var _useState = _sliced_to_array((0, _react.useState)(null), 2), showActionsPopover = _useState[0], setShowActionsPopover = _useState[1];
125
- (0, _react.useEffect)(function() {
126
- var width = expanded ? 'var(--litho-pane-width-expanded)' : 'var(--litho-pane-width-default)';
127
- document.documentElement.style.setProperty('--litho-pane-width', width);
128
- }, [
129
- expanded
130
- ]);
131
734
  var backAction = _backAction ? /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
132
735
  className: "Litho-Page__Header--BackAction",
133
736
  children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_Button.default, {
@@ -143,10 +746,10 @@ function Header() {
143
746
  className: "Litho-Pane__Header",
144
747
  children: [
145
748
  /*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
146
- className: "flex items-center justify-between gap-2 min-h-14 px-2 border-edge-subdued ".concat(bottomBorder ? "border-b" : ""),
749
+ className: "flex items-center justify-between px-2 border-edge-subdued min-h-14 ".concat(bottomBorder ? "border-b" : ""),
147
750
  children: [
148
751
  /*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
149
- className: "flex items-center gap-2 flex-1",
752
+ className: "flex items-center gap-2",
150
753
  children: [
151
754
  backAction,
152
755
  /*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
@@ -168,62 +771,43 @@ function Header() {
168
771
  })
169
772
  ]
170
773
  }),
171
- showActionsOrClose && /*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
774
+ /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
172
775
  className: "flex items-center justify-end",
173
- children: [
174
- actions.map(function(action, index) {
175
- var showPopover = action.subActions || action.subSections;
176
- var innerContent = /*#__PURE__*/ (0, _jsxruntime.jsx)(_Button.default, {
177
- plain: !action.destructive,
178
- onClick: showPopover ? function() {
179
- return setShowActionsPopover(index);
180
- } : action.onAction || action.onClick,
181
- disabled: action.disabled,
182
- icon: action.icon,
183
- loading: action.loading,
184
- external: action.external,
185
- url: action.url,
186
- destructive: action.destructive,
187
- tooltip: action.tooltip && showActionsPopover !== index ? action.tooltip : undefined,
188
- tooltipPosition: "below"
189
- }, index);
190
- if (showPopover) {
191
- return /*#__PURE__*/ (0, _jsxruntime.jsx)(_Popover.default, {
192
- active: showActionsPopover === index,
193
- onClose: function() {
194
- return setShowActionsPopover(null);
195
- },
196
- activator: innerContent,
197
- preferredAlignment: "left",
198
- children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_ActionList.default, {
199
- sections: action.subSections,
200
- items: action.subActions,
201
- onActionAnyItem: function() {
202
- return setShowActionsPopover(null);
203
- }
204
- })
205
- });
206
- }
207
- return innerContent;
208
- }),
209
- allowExpand && /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
210
- className: "hidden xl:block",
211
- children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_Button.default, {
212
- plain: true,
213
- icon: expanded ? _polarisicons.MinimizeMajor : _polarisicons.MaximizeMajor,
214
- onClick: onToggleExpand,
215
- tooltip: expanded ? "Minimize" : "Expand",
216
- tooltipPosition: "below"
217
- })
218
- }),
219
- showCloseButton && /*#__PURE__*/ (0, _jsxruntime.jsx)(_Button.default, {
220
- plain: true,
221
- icon: _polarisicons.CancelMajor,
222
- onClick: onClose,
223
- tooltip: "Close",
776
+ children: actions.length > 0 && actions.map(function(action, index) {
777
+ var showPopover = action.subActions || action.subSections;
778
+ var innerContent = /*#__PURE__*/ (0, _jsxruntime.jsx)(_Button.default, {
779
+ plain: !action.destructive,
780
+ onClick: showPopover ? function() {
781
+ return setShowActionsPopover(index);
782
+ } : action.onAction || action.onClick,
783
+ disabled: action.disabled,
784
+ icon: action.icon,
785
+ loading: action.loading,
786
+ external: action.external,
787
+ url: action.url,
788
+ destructive: action.destructive,
789
+ tooltip: action.tooltip && showActionsPopover !== index ? action.tooltip : undefined,
224
790
  tooltipPosition: "below"
225
- })
226
- ]
791
+ }, index);
792
+ if (showPopover) {
793
+ return /*#__PURE__*/ (0, _jsxruntime.jsx)(_Popover.default, {
794
+ active: showActionsPopover === index,
795
+ onClose: function() {
796
+ return setShowActionsPopover(null);
797
+ },
798
+ activator: innerContent,
799
+ preferredAlignment: "left",
800
+ children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_ActionList.default, {
801
+ sections: action.subSections,
802
+ items: action.subActions,
803
+ onActionAnyItem: function() {
804
+ return setShowActionsPopover(null);
805
+ }
806
+ })
807
+ });
808
+ }
809
+ return innerContent;
810
+ })
227
811
  })
228
812
  ]
229
813
  }),
@@ -239,7 +823,7 @@ function Content() {
239
823
  var props = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
240
824
  var children = props.children, _props_padded = props.padded, padded = _props_padded === void 0 ? true : _props_padded, className = props.className;
241
825
  return /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
242
- className: "Litho-Pane__Content flex-1 overflow-auto ".concat(padded ? "p-2" : "", " ").concat(className),
826
+ className: "Litho-Pane__Content flex-1 overflow-auto ".concat(padded ? "p-2" : "", " ").concat(className || ""),
243
827
  children: children
244
828
  });
245
829
  }
@@ -247,8 +831,9 @@ Pane.Content = Content;
247
831
  function Footer() {
248
832
  var props = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
249
833
  var children = props.children, _props_topBorder = props.topBorder, topBorder = _props_topBorder === void 0 ? true : _props_topBorder;
834
+ var isMobile = (0, _useMobile.useMobile)(MOBILE_BREAKPOINT);
250
835
  return /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
251
- className: "Litho-Pane__Footer min-h-12 ".concat(topBorder ? "border-t border-edge-subdued" : ""),
836
+ className: "Litho-Pane__Footer min-h-12 ".concat(topBorder ? "border-t border-edge-subdued" : "", " ").concat(isMobile ? "sticky bottom-0 bg-surface-lowest z-10" : ""),
252
837
  children: children
253
838
  });
254
839
  }