react-resizable-panels.browser.cjs.js 82 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var React = require('react');
  4. function _interopNamespace(e) {
  5. if (e && e.__esModule) return e;
  6. var n = Object.create(null);
  7. if (e) {
  8. Object.keys(e).forEach(function (k) {
  9. if (k !== 'default') {
  10. var d = Object.getOwnPropertyDescriptor(e, k);
  11. Object.defineProperty(n, k, d.get ? d : {
  12. enumerable: true,
  13. get: function () { return e[k]; }
  14. });
  15. }
  16. });
  17. }
  18. n["default"] = e;
  19. return Object.freeze(n);
  20. }
  21. var React__namespace = /*#__PURE__*/_interopNamespace(React);
  22. // The "contextmenu" event is not supported as a PointerEvent in all browsers yet, so MouseEvent still need to be handled
  23. const PanelGroupContext = React.createContext(null);
  24. PanelGroupContext.displayName = "PanelGroupContext";
  25. const DATA_ATTRIBUTES = {
  26. group: "data-panel-group",
  27. groupDirection: "data-panel-group-direction",
  28. groupId: "data-panel-group-id",
  29. panel: "data-panel",
  30. panelCollapsible: "data-panel-collapsible",
  31. panelId: "data-panel-id",
  32. panelSize: "data-panel-size",
  33. resizeHandle: "data-resize-handle",
  34. resizeHandleActive: "data-resize-handle-active",
  35. resizeHandleEnabled: "data-panel-resize-handle-enabled",
  36. resizeHandleId: "data-panel-resize-handle-id",
  37. resizeHandleState: "data-resize-handle-state"
  38. };
  39. const PRECISION = 10;
  40. const useIsomorphicLayoutEffect = React.useLayoutEffect ;
  41. const useId = React__namespace["useId".toString()];
  42. const wrappedUseId = typeof useId === "function" ? useId : () => null;
  43. let counter = 0;
  44. function useUniqueId(idFromParams = null) {
  45. const idFromUseId = wrappedUseId();
  46. const idRef = React.useRef(idFromParams || idFromUseId || null);
  47. if (idRef.current === null) {
  48. idRef.current = "" + counter++;
  49. }
  50. return idFromParams !== null && idFromParams !== void 0 ? idFromParams : idRef.current;
  51. }
  52. function PanelWithForwardedRef({
  53. children,
  54. className: classNameFromProps = "",
  55. collapsedSize,
  56. collapsible,
  57. defaultSize,
  58. forwardedRef,
  59. id: idFromProps,
  60. maxSize,
  61. minSize,
  62. onCollapse,
  63. onExpand,
  64. onResize,
  65. order,
  66. style: styleFromProps,
  67. tagName: Type = "div",
  68. ...rest
  69. }) {
  70. const context = React.useContext(PanelGroupContext);
  71. if (context === null) {
  72. throw Error(`Panel components must be rendered within a PanelGroup container`);
  73. }
  74. const {
  75. collapsePanel,
  76. expandPanel,
  77. getPanelSize,
  78. getPanelStyle,
  79. groupId,
  80. isPanelCollapsed,
  81. reevaluatePanelConstraints,
  82. registerPanel,
  83. resizePanel,
  84. unregisterPanel
  85. } = context;
  86. const panelId = useUniqueId(idFromProps);
  87. const panelDataRef = React.useRef({
  88. callbacks: {
  89. onCollapse,
  90. onExpand,
  91. onResize
  92. },
  93. constraints: {
  94. collapsedSize,
  95. collapsible,
  96. defaultSize,
  97. maxSize,
  98. minSize
  99. },
  100. id: panelId,
  101. idIsFromProps: idFromProps !== undefined,
  102. order
  103. });
  104. React.useRef({
  105. didLogMissingDefaultSizeWarning: false
  106. });
  107. useIsomorphicLayoutEffect(() => {
  108. const {
  109. callbacks,
  110. constraints
  111. } = panelDataRef.current;
  112. const prevConstraints = {
  113. ...constraints
  114. };
  115. panelDataRef.current.id = panelId;
  116. panelDataRef.current.idIsFromProps = idFromProps !== undefined;
  117. panelDataRef.current.order = order;
  118. callbacks.onCollapse = onCollapse;
  119. callbacks.onExpand = onExpand;
  120. callbacks.onResize = onResize;
  121. constraints.collapsedSize = collapsedSize;
  122. constraints.collapsible = collapsible;
  123. constraints.defaultSize = defaultSize;
  124. constraints.maxSize = maxSize;
  125. constraints.minSize = minSize;
  126. // If constraints have changed, we should revisit panel sizes.
  127. // This is uncommon but may happen if people are trying to implement pixel based constraints.
  128. if (prevConstraints.collapsedSize !== constraints.collapsedSize || prevConstraints.collapsible !== constraints.collapsible || prevConstraints.maxSize !== constraints.maxSize || prevConstraints.minSize !== constraints.minSize) {
  129. reevaluatePanelConstraints(panelDataRef.current, prevConstraints);
  130. }
  131. });
  132. useIsomorphicLayoutEffect(() => {
  133. const panelData = panelDataRef.current;
  134. registerPanel(panelData);
  135. return () => {
  136. unregisterPanel(panelData);
  137. };
  138. }, [order, panelId, registerPanel, unregisterPanel]);
  139. React.useImperativeHandle(forwardedRef, () => ({
  140. collapse: () => {
  141. collapsePanel(panelDataRef.current);
  142. },
  143. expand: minSize => {
  144. expandPanel(panelDataRef.current, minSize);
  145. },
  146. getId() {
  147. return panelId;
  148. },
  149. getSize() {
  150. return getPanelSize(panelDataRef.current);
  151. },
  152. isCollapsed() {
  153. return isPanelCollapsed(panelDataRef.current);
  154. },
  155. isExpanded() {
  156. return !isPanelCollapsed(panelDataRef.current);
  157. },
  158. resize: size => {
  159. resizePanel(panelDataRef.current, size);
  160. }
  161. }), [collapsePanel, expandPanel, getPanelSize, isPanelCollapsed, panelId, resizePanel]);
  162. const style = getPanelStyle(panelDataRef.current, defaultSize);
  163. return React.createElement(Type, {
  164. ...rest,
  165. children,
  166. className: classNameFromProps,
  167. id: panelId,
  168. style: {
  169. ...style,
  170. ...styleFromProps
  171. },
  172. // CSS selectors
  173. [DATA_ATTRIBUTES.groupId]: groupId,
  174. [DATA_ATTRIBUTES.panel]: "",
  175. [DATA_ATTRIBUTES.panelCollapsible]: collapsible || undefined,
  176. [DATA_ATTRIBUTES.panelId]: panelId,
  177. [DATA_ATTRIBUTES.panelSize]: parseFloat("" + style.flexGrow).toFixed(1)
  178. });
  179. }
  180. const Panel = React.forwardRef((props, ref) => React.createElement(PanelWithForwardedRef, {
  181. ...props,
  182. forwardedRef: ref
  183. }));
  184. PanelWithForwardedRef.displayName = "Panel";
  185. Panel.displayName = "forwardRef(Panel)";
  186. let nonce;
  187. function getNonce() {
  188. return nonce;
  189. }
  190. function setNonce(value) {
  191. nonce = value;
  192. }
  193. let currentCursorStyle = null;
  194. let enabled = true;
  195. let prevRuleIndex = -1;
  196. let styleElement = null;
  197. function disableGlobalCursorStyles() {
  198. enabled = false;
  199. }
  200. function enableGlobalCursorStyles() {
  201. enabled = true;
  202. }
  203. function getCursorStyle(state, constraintFlags) {
  204. if (constraintFlags) {
  205. const horizontalMin = (constraintFlags & EXCEEDED_HORIZONTAL_MIN) !== 0;
  206. const horizontalMax = (constraintFlags & EXCEEDED_HORIZONTAL_MAX) !== 0;
  207. const verticalMin = (constraintFlags & EXCEEDED_VERTICAL_MIN) !== 0;
  208. const verticalMax = (constraintFlags & EXCEEDED_VERTICAL_MAX) !== 0;
  209. if (horizontalMin) {
  210. if (verticalMin) {
  211. return "se-resize";
  212. } else if (verticalMax) {
  213. return "ne-resize";
  214. } else {
  215. return "e-resize";
  216. }
  217. } else if (horizontalMax) {
  218. if (verticalMin) {
  219. return "sw-resize";
  220. } else if (verticalMax) {
  221. return "nw-resize";
  222. } else {
  223. return "w-resize";
  224. }
  225. } else if (verticalMin) {
  226. return "s-resize";
  227. } else if (verticalMax) {
  228. return "n-resize";
  229. }
  230. }
  231. switch (state) {
  232. case "horizontal":
  233. return "ew-resize";
  234. case "intersection":
  235. return "move";
  236. case "vertical":
  237. return "ns-resize";
  238. }
  239. }
  240. function resetGlobalCursorStyle() {
  241. if (styleElement !== null) {
  242. document.head.removeChild(styleElement);
  243. currentCursorStyle = null;
  244. styleElement = null;
  245. prevRuleIndex = -1;
  246. }
  247. }
  248. function setGlobalCursorStyle(state, constraintFlags) {
  249. var _styleElement$sheet$i, _styleElement$sheet2;
  250. if (!enabled) {
  251. return;
  252. }
  253. const style = getCursorStyle(state, constraintFlags);
  254. if (currentCursorStyle === style) {
  255. return;
  256. }
  257. currentCursorStyle = style;
  258. if (styleElement === null) {
  259. styleElement = document.createElement("style");
  260. const nonce = getNonce();
  261. if (nonce) {
  262. styleElement.setAttribute("nonce", nonce);
  263. }
  264. document.head.appendChild(styleElement);
  265. }
  266. if (prevRuleIndex >= 0) {
  267. var _styleElement$sheet;
  268. (_styleElement$sheet = styleElement.sheet) === null || _styleElement$sheet === void 0 ? void 0 : _styleElement$sheet.removeRule(prevRuleIndex);
  269. }
  270. prevRuleIndex = (_styleElement$sheet$i = (_styleElement$sheet2 = styleElement.sheet) === null || _styleElement$sheet2 === void 0 ? void 0 : _styleElement$sheet2.insertRule(`*{cursor: ${style} !important;}`)) !== null && _styleElement$sheet$i !== void 0 ? _styleElement$sheet$i : -1;
  271. }
  272. function isKeyDown(event) {
  273. return event.type === "keydown";
  274. }
  275. function isPointerEvent(event) {
  276. return event.type.startsWith("pointer");
  277. }
  278. function isMouseEvent(event) {
  279. return event.type.startsWith("mouse");
  280. }
  281. function getResizeEventCoordinates(event) {
  282. if (isPointerEvent(event)) {
  283. if (event.isPrimary) {
  284. return {
  285. x: event.clientX,
  286. y: event.clientY
  287. };
  288. }
  289. } else if (isMouseEvent(event)) {
  290. return {
  291. x: event.clientX,
  292. y: event.clientY
  293. };
  294. }
  295. return {
  296. x: Infinity,
  297. y: Infinity
  298. };
  299. }
  300. function getInputType() {
  301. if (typeof matchMedia === "function") {
  302. return matchMedia("(pointer:coarse)").matches ? "coarse" : "fine";
  303. }
  304. }
  305. function intersects(rectOne, rectTwo, strict) {
  306. if (strict) {
  307. return rectOne.x < rectTwo.x + rectTwo.width && rectOne.x + rectOne.width > rectTwo.x && rectOne.y < rectTwo.y + rectTwo.height && rectOne.y + rectOne.height > rectTwo.y;
  308. } else {
  309. return rectOne.x <= rectTwo.x + rectTwo.width && rectOne.x + rectOne.width >= rectTwo.x && rectOne.y <= rectTwo.y + rectTwo.height && rectOne.y + rectOne.height >= rectTwo.y;
  310. }
  311. }
  312. // Forked from NPM stacking-order@2.0.0
  313. /**
  314. * Determine which of two nodes appears in front of the other —
  315. * if `a` is in front, returns 1, otherwise returns -1
  316. * @param {HTMLElement | SVGElement} a
  317. * @param {HTMLElement | SVGElement} b
  318. */
  319. function compare(a, b) {
  320. if (a === b) throw new Error("Cannot compare node with itself");
  321. const ancestors = {
  322. a: get_ancestors(a),
  323. b: get_ancestors(b)
  324. };
  325. let common_ancestor;
  326. // remove shared ancestors
  327. while (ancestors.a.at(-1) === ancestors.b.at(-1)) {
  328. a = ancestors.a.pop();
  329. b = ancestors.b.pop();
  330. common_ancestor = a;
  331. }
  332. assert(common_ancestor, "Stacking order can only be calculated for elements with a common ancestor");
  333. const z_indexes = {
  334. a: get_z_index(find_stacking_context(ancestors.a)),
  335. b: get_z_index(find_stacking_context(ancestors.b))
  336. };
  337. if (z_indexes.a === z_indexes.b) {
  338. const children = common_ancestor.childNodes;
  339. const furthest_ancestors = {
  340. a: ancestors.a.at(-1),
  341. b: ancestors.b.at(-1)
  342. };
  343. let i = children.length;
  344. while (i--) {
  345. const child = children[i];
  346. if (child === furthest_ancestors.a) return 1;
  347. if (child === furthest_ancestors.b) return -1;
  348. }
  349. }
  350. return Math.sign(z_indexes.a - z_indexes.b);
  351. }
  352. const props = /\b(?:position|zIndex|opacity|transform|webkitTransform|mixBlendMode|filter|webkitFilter|isolation)\b/;
  353. /** @param {HTMLElement | SVGElement} node */
  354. function is_flex_item(node) {
  355. var _get_parent;
  356. // @ts-ignore
  357. const display = getComputedStyle((_get_parent = get_parent(node)) !== null && _get_parent !== void 0 ? _get_parent : node).display;
  358. return display === "flex" || display === "inline-flex";
  359. }
  360. /** @param {HTMLElement | SVGElement} node */
  361. function creates_stacking_context(node) {
  362. const style = getComputedStyle(node);
  363. // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
  364. if (style.position === "fixed") return true;
  365. // Forked to fix upstream bug https://github.com/Rich-Harris/stacking-order/issues/3
  366. // if (
  367. // (style.zIndex !== "auto" && style.position !== "static") ||
  368. // is_flex_item(node)
  369. // )
  370. if (style.zIndex !== "auto" && (style.position !== "static" || is_flex_item(node))) return true;
  371. if (+style.opacity < 1) return true;
  372. if ("transform" in style && style.transform !== "none") return true;
  373. if ("webkitTransform" in style && style.webkitTransform !== "none") return true;
  374. if ("mixBlendMode" in style && style.mixBlendMode !== "normal") return true;
  375. if ("filter" in style && style.filter !== "none") return true;
  376. if ("webkitFilter" in style && style.webkitFilter !== "none") return true;
  377. if ("isolation" in style && style.isolation === "isolate") return true;
  378. if (props.test(style.willChange)) return true;
  379. // @ts-expect-error
  380. if (style.webkitOverflowScrolling === "touch") return true;
  381. return false;
  382. }
  383. /** @param {(HTMLElement| SVGElement)[]} nodes */
  384. function find_stacking_context(nodes) {
  385. let i = nodes.length;
  386. while (i--) {
  387. const node = nodes[i];
  388. assert(node, "Missing node");
  389. if (creates_stacking_context(node)) return node;
  390. }
  391. return null;
  392. }
  393. /** @param {HTMLElement | SVGElement} node */
  394. function get_z_index(node) {
  395. return node && Number(getComputedStyle(node).zIndex) || 0;
  396. }
  397. /** @param {HTMLElement} node */
  398. function get_ancestors(node) {
  399. const ancestors = [];
  400. while (node) {
  401. ancestors.push(node);
  402. // @ts-ignore
  403. node = get_parent(node);
  404. }
  405. return ancestors; // [ node, ... <body>, <html>, document ]
  406. }
  407. /** @param {HTMLElement} node */
  408. function get_parent(node) {
  409. const {
  410. parentNode
  411. } = node;
  412. if (parentNode && parentNode instanceof ShadowRoot) {
  413. return parentNode.host;
  414. }
  415. return parentNode;
  416. }
  417. const EXCEEDED_HORIZONTAL_MIN = 0b0001;
  418. const EXCEEDED_HORIZONTAL_MAX = 0b0010;
  419. const EXCEEDED_VERTICAL_MIN = 0b0100;
  420. const EXCEEDED_VERTICAL_MAX = 0b1000;
  421. const isCoarsePointer = getInputType() === "coarse";
  422. let intersectingHandles = [];
  423. let isPointerDown = false;
  424. let ownerDocumentCounts = new Map();
  425. let panelConstraintFlags = new Map();
  426. const registeredResizeHandlers = new Set();
  427. function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins, setResizeHandlerState) {
  428. var _ownerDocumentCounts$;
  429. const {
  430. ownerDocument
  431. } = element;
  432. const data = {
  433. direction,
  434. element,
  435. hitAreaMargins,
  436. setResizeHandlerState
  437. };
  438. const count = (_ownerDocumentCounts$ = ownerDocumentCounts.get(ownerDocument)) !== null && _ownerDocumentCounts$ !== void 0 ? _ownerDocumentCounts$ : 0;
  439. ownerDocumentCounts.set(ownerDocument, count + 1);
  440. registeredResizeHandlers.add(data);
  441. updateListeners();
  442. return function unregisterResizeHandle() {
  443. var _ownerDocumentCounts$2;
  444. panelConstraintFlags.delete(resizeHandleId);
  445. registeredResizeHandlers.delete(data);
  446. const count = (_ownerDocumentCounts$2 = ownerDocumentCounts.get(ownerDocument)) !== null && _ownerDocumentCounts$2 !== void 0 ? _ownerDocumentCounts$2 : 1;
  447. ownerDocumentCounts.set(ownerDocument, count - 1);
  448. updateListeners();
  449. if (count === 1) {
  450. ownerDocumentCounts.delete(ownerDocument);
  451. }
  452. // If the resize handle that is currently unmounting is intersecting with the pointer,
  453. // update the global pointer to account for the change
  454. if (intersectingHandles.includes(data)) {
  455. const index = intersectingHandles.indexOf(data);
  456. if (index >= 0) {
  457. intersectingHandles.splice(index, 1);
  458. }
  459. updateCursor();
  460. // Also instruct the handle to stop dragging; this prevents the parent group from being left in an inconsistent state
  461. // See github.com/bvaughn/react-resizable-panels/issues/402
  462. setResizeHandlerState("up", true, null);
  463. }
  464. };
  465. }
  466. function handlePointerDown(event) {
  467. const {
  468. target
  469. } = event;
  470. const {
  471. x,
  472. y
  473. } = getResizeEventCoordinates(event);
  474. isPointerDown = true;
  475. recalculateIntersectingHandles({
  476. target,
  477. x,
  478. y
  479. });
  480. updateListeners();
  481. if (intersectingHandles.length > 0) {
  482. updateResizeHandlerStates("down", event);
  483. event.preventDefault();
  484. if (!isWithinResizeHandle(target)) {
  485. event.stopImmediatePropagation();
  486. }
  487. }
  488. }
  489. function handlePointerMove(event) {
  490. const {
  491. x,
  492. y
  493. } = getResizeEventCoordinates(event);
  494. // Edge case (see #340)
  495. // Detect when the pointer has been released outside an iframe on a different domain
  496. if (isPointerDown && event.buttons === 0) {
  497. isPointerDown = false;
  498. updateResizeHandlerStates("up", event);
  499. }
  500. if (!isPointerDown) {
  501. const {
  502. target
  503. } = event;
  504. // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
  505. // at that point, the handles may not move with the pointer (depending on constraints)
  506. // but the same set of active handles should be locked until the pointer is released
  507. recalculateIntersectingHandles({
  508. target,
  509. x,
  510. y
  511. });
  512. }
  513. updateResizeHandlerStates("move", event);
  514. // Update cursor based on return value(s) from active handles
  515. updateCursor();
  516. if (intersectingHandles.length > 0) {
  517. event.preventDefault();
  518. }
  519. }
  520. function handlePointerUp(event) {
  521. const {
  522. target
  523. } = event;
  524. const {
  525. x,
  526. y
  527. } = getResizeEventCoordinates(event);
  528. panelConstraintFlags.clear();
  529. isPointerDown = false;
  530. if (intersectingHandles.length > 0) {
  531. event.preventDefault();
  532. if (!isWithinResizeHandle(target)) {
  533. event.stopImmediatePropagation();
  534. }
  535. }
  536. updateResizeHandlerStates("up", event);
  537. recalculateIntersectingHandles({
  538. target,
  539. x,
  540. y
  541. });
  542. updateCursor();
  543. updateListeners();
  544. }
  545. function isWithinResizeHandle(element) {
  546. let currentElement = element;
  547. while (currentElement) {
  548. if (currentElement.hasAttribute(DATA_ATTRIBUTES.resizeHandle)) {
  549. return true;
  550. }
  551. currentElement = currentElement.parentElement;
  552. }
  553. return false;
  554. }
  555. function recalculateIntersectingHandles({
  556. target,
  557. x,
  558. y
  559. }) {
  560. intersectingHandles.splice(0);
  561. let targetElement = null;
  562. if (target instanceof HTMLElement || target instanceof SVGElement) {
  563. targetElement = target;
  564. }
  565. registeredResizeHandlers.forEach(data => {
  566. const {
  567. element: dragHandleElement,
  568. hitAreaMargins
  569. } = data;
  570. const dragHandleRect = dragHandleElement.getBoundingClientRect();
  571. const {
  572. bottom,
  573. left,
  574. right,
  575. top
  576. } = dragHandleRect;
  577. const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
  578. const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
  579. if (eventIntersects) {
  580. // TRICKY
  581. // We listen for pointers events at the root in order to support hit area margins
  582. // (determining when the pointer is close enough to an element to be considered a "hit")
  583. // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
  584. // so at this point we need to compare stacking order of a potentially intersecting drag handle,
  585. // and the element that was actually clicked/touched
  586. if (targetElement !== null && document.contains(targetElement) && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
  587. // Calculating stacking order has a cost, so we should avoid it if possible
  588. // That is why we only check potentially intersecting handles,
  589. // and why we skip if the event target is within the handle's DOM
  590. compare(targetElement, dragHandleElement) > 0) {
  591. // If the target is above the drag handle, then we also need to confirm they overlap
  592. // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
  593. //
  594. // It's not enough to compare only the target
  595. // The target might be a small element inside of a larger container
  596. // (For example, a SPAN or a DIV inside of a larger modal dialog)
  597. let currentElement = targetElement;
  598. let didIntersect = false;
  599. while (currentElement) {
  600. if (currentElement.contains(dragHandleElement)) {
  601. break;
  602. } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
  603. didIntersect = true;
  604. break;
  605. }
  606. currentElement = currentElement.parentElement;
  607. }
  608. if (didIntersect) {
  609. return;
  610. }
  611. }
  612. intersectingHandles.push(data);
  613. }
  614. });
  615. }
  616. function reportConstraintsViolation(resizeHandleId, flag) {
  617. panelConstraintFlags.set(resizeHandleId, flag);
  618. }
  619. function updateCursor() {
  620. let intersectsHorizontal = false;
  621. let intersectsVertical = false;
  622. intersectingHandles.forEach(data => {
  623. const {
  624. direction
  625. } = data;
  626. if (direction === "horizontal") {
  627. intersectsHorizontal = true;
  628. } else {
  629. intersectsVertical = true;
  630. }
  631. });
  632. let constraintFlags = 0;
  633. panelConstraintFlags.forEach(flag => {
  634. constraintFlags |= flag;
  635. });
  636. if (intersectsHorizontal && intersectsVertical) {
  637. setGlobalCursorStyle("intersection", constraintFlags);
  638. } else if (intersectsHorizontal) {
  639. setGlobalCursorStyle("horizontal", constraintFlags);
  640. } else if (intersectsVertical) {
  641. setGlobalCursorStyle("vertical", constraintFlags);
  642. } else {
  643. resetGlobalCursorStyle();
  644. }
  645. }
  646. let listenersAbortController = new AbortController();
  647. function updateListeners() {
  648. listenersAbortController.abort();
  649. listenersAbortController = new AbortController();
  650. const options = {
  651. capture: true,
  652. signal: listenersAbortController.signal
  653. };
  654. if (!registeredResizeHandlers.size) {
  655. return;
  656. }
  657. if (isPointerDown) {
  658. if (intersectingHandles.length > 0) {
  659. ownerDocumentCounts.forEach((count, ownerDocument) => {
  660. const {
  661. body
  662. } = ownerDocument;
  663. if (count > 0) {
  664. body.addEventListener("contextmenu", handlePointerUp, options);
  665. body.addEventListener("pointerleave", handlePointerMove, options);
  666. body.addEventListener("pointermove", handlePointerMove, options);
  667. }
  668. });
  669. }
  670. window.addEventListener("pointerup", handlePointerUp, options);
  671. window.addEventListener("pointercancel", handlePointerUp, options);
  672. } else {
  673. ownerDocumentCounts.forEach((count, ownerDocument) => {
  674. const {
  675. body
  676. } = ownerDocument;
  677. if (count > 0) {
  678. body.addEventListener("pointerdown", handlePointerDown, options);
  679. body.addEventListener("pointermove", handlePointerMove, options);
  680. }
  681. });
  682. }
  683. }
  684. function updateResizeHandlerStates(action, event) {
  685. registeredResizeHandlers.forEach(data => {
  686. const {
  687. setResizeHandlerState
  688. } = data;
  689. const isActive = intersectingHandles.includes(data);
  690. setResizeHandlerState(action, isActive, event);
  691. });
  692. }
  693. function useForceUpdate() {
  694. const [_, setCount] = React.useState(0);
  695. return React.useCallback(() => setCount(prevCount => prevCount + 1), []);
  696. }
  697. function assert(expectedCondition, message) {
  698. if (!expectedCondition) {
  699. console.error(message);
  700. throw Error(message);
  701. }
  702. }
  703. function fuzzyCompareNumbers(actual, expected, fractionDigits = PRECISION) {
  704. if (actual.toFixed(fractionDigits) === expected.toFixed(fractionDigits)) {
  705. return 0;
  706. } else {
  707. return actual > expected ? 1 : -1;
  708. }
  709. }
  710. function fuzzyNumbersEqual$1(actual, expected, fractionDigits = PRECISION) {
  711. return fuzzyCompareNumbers(actual, expected, fractionDigits) === 0;
  712. }
  713. function fuzzyNumbersEqual(actual, expected, fractionDigits) {
  714. return fuzzyCompareNumbers(actual, expected, fractionDigits) === 0;
  715. }
  716. function fuzzyLayoutsEqual(actual, expected, fractionDigits) {
  717. if (actual.length !== expected.length) {
  718. return false;
  719. }
  720. for (let index = 0; index < actual.length; index++) {
  721. const actualSize = actual[index];
  722. const expectedSize = expected[index];
  723. if (!fuzzyNumbersEqual(actualSize, expectedSize, fractionDigits)) {
  724. return false;
  725. }
  726. }
  727. return true;
  728. }
  729. // Panel size must be in percentages; pixel values should be pre-converted
  730. function resizePanel({
  731. panelConstraints: panelConstraintsArray,
  732. panelIndex,
  733. size
  734. }) {
  735. const panelConstraints = panelConstraintsArray[panelIndex];
  736. assert(panelConstraints != null, `Panel constraints not found for index ${panelIndex}`);
  737. let {
  738. collapsedSize = 0,
  739. collapsible,
  740. maxSize = 100,
  741. minSize = 0
  742. } = panelConstraints;
  743. if (fuzzyCompareNumbers(size, minSize) < 0) {
  744. if (collapsible) {
  745. // Collapsible panels should snap closed or open only once they cross the halfway point between collapsed and min size.
  746. const halfwayPoint = (collapsedSize + minSize) / 2;
  747. if (fuzzyCompareNumbers(size, halfwayPoint) < 0) {
  748. size = collapsedSize;
  749. } else {
  750. size = minSize;
  751. }
  752. } else {
  753. size = minSize;
  754. }
  755. }
  756. size = Math.min(maxSize, size);
  757. size = parseFloat(size.toFixed(PRECISION));
  758. return size;
  759. }
  760. // All units must be in percentages; pixel values should be pre-converted
  761. function adjustLayoutByDelta({
  762. delta,
  763. initialLayout,
  764. panelConstraints: panelConstraintsArray,
  765. pivotIndices,
  766. prevLayout,
  767. trigger
  768. }) {
  769. if (fuzzyNumbersEqual(delta, 0)) {
  770. return initialLayout;
  771. }
  772. const nextLayout = [...initialLayout];
  773. const [firstPivotIndex, secondPivotIndex] = pivotIndices;
  774. assert(firstPivotIndex != null, "Invalid first pivot index");
  775. assert(secondPivotIndex != null, "Invalid second pivot index");
  776. let deltaApplied = 0;
  777. // const DEBUG = [];
  778. // DEBUG.push(`adjustLayoutByDelta()`);
  779. // DEBUG.push(` initialLayout: ${initialLayout.join(", ")}`);
  780. // DEBUG.push(` prevLayout: ${prevLayout.join(", ")}`);
  781. // DEBUG.push(` delta: ${delta}`);
  782. // DEBUG.push(` pivotIndices: ${pivotIndices.join(", ")}`);
  783. // DEBUG.push(` trigger: ${trigger}`);
  784. // DEBUG.push("");
  785. // A resizing panel affects the panels before or after it.
  786. //
  787. // A negative delta means the panel(s) immediately after the resize handle should grow/expand by decreasing its offset.
  788. // Other panels may also need to shrink/contract (and shift) to make room, depending on the min weights.
  789. //
  790. // A positive delta means the panel(s) immediately before the resize handle should "expand".
  791. // This is accomplished by shrinking/contracting (and shifting) one or more of the panels after the resize handle.
  792. {
  793. // If this is a resize triggered by a keyboard event, our logic for expanding/collapsing is different.
  794. // We no longer check the halfway threshold because this may prevent the panel from expanding at all.
  795. if (trigger === "keyboard") {
  796. {
  797. // Check if we should expand a collapsed panel
  798. const index = delta < 0 ? secondPivotIndex : firstPivotIndex;
  799. const panelConstraints = panelConstraintsArray[index];
  800. assert(panelConstraints, `Panel constraints not found for index ${index}`);
  801. const {
  802. collapsedSize = 0,
  803. collapsible,
  804. minSize = 0
  805. } = panelConstraints;
  806. // DEBUG.push(`edge case check 1: ${index}`);
  807. // DEBUG.push(` -> collapsible? ${collapsible}`);
  808. if (collapsible) {
  809. const prevSize = initialLayout[index];
  810. assert(prevSize != null, `Previous layout not found for panel index ${index}`);
  811. if (fuzzyNumbersEqual(prevSize, collapsedSize)) {
  812. const localDelta = minSize - prevSize;
  813. // DEBUG.push(` -> expand delta: ${localDelta}`);
  814. if (fuzzyCompareNumbers(localDelta, Math.abs(delta)) > 0) {
  815. delta = delta < 0 ? 0 - localDelta : localDelta;
  816. // DEBUG.push(` -> delta: ${delta}`);
  817. }
  818. }
  819. }
  820. }
  821. {
  822. // Check if we should collapse a panel at its minimum size
  823. const index = delta < 0 ? firstPivotIndex : secondPivotIndex;
  824. const panelConstraints = panelConstraintsArray[index];
  825. assert(panelConstraints, `No panel constraints found for index ${index}`);
  826. const {
  827. collapsedSize = 0,
  828. collapsible,
  829. minSize = 0
  830. } = panelConstraints;
  831. // DEBUG.push(`edge case check 2: ${index}`);
  832. // DEBUG.push(` -> collapsible? ${collapsible}`);
  833. if (collapsible) {
  834. const prevSize = initialLayout[index];
  835. assert(prevSize != null, `Previous layout not found for panel index ${index}`);
  836. if (fuzzyNumbersEqual(prevSize, minSize)) {
  837. const localDelta = prevSize - collapsedSize;
  838. // DEBUG.push(` -> expand delta: ${localDelta}`);
  839. if (fuzzyCompareNumbers(localDelta, Math.abs(delta)) > 0) {
  840. delta = delta < 0 ? 0 - localDelta : localDelta;
  841. // DEBUG.push(` -> delta: ${delta}`);
  842. }
  843. }
  844. }
  845. }
  846. }
  847. // DEBUG.push("");
  848. }
  849. {
  850. // Pre-calculate max available delta in the opposite direction of our pivot.
  851. // This will be the maximum amount we're allowed to expand/contract the panels in the primary direction.
  852. // If this amount is less than the requested delta, adjust the requested delta.
  853. // If this amount is greater than the requested delta, that's useful information too–
  854. // as an expanding panel might change from collapsed to min size.
  855. const increment = delta < 0 ? 1 : -1;
  856. let index = delta < 0 ? secondPivotIndex : firstPivotIndex;
  857. let maxAvailableDelta = 0;
  858. // DEBUG.push("pre calc...");
  859. while (true) {
  860. const prevSize = initialLayout[index];
  861. assert(prevSize != null, `Previous layout not found for panel index ${index}`);
  862. const maxSafeSize = resizePanel({
  863. panelConstraints: panelConstraintsArray,
  864. panelIndex: index,
  865. size: 100
  866. });
  867. const delta = maxSafeSize - prevSize;
  868. // DEBUG.push(` ${index}: ${prevSize} -> ${maxSafeSize}`);
  869. maxAvailableDelta += delta;
  870. index += increment;
  871. if (index < 0 || index >= panelConstraintsArray.length) {
  872. break;
  873. }
  874. }
  875. // DEBUG.push(` -> max available delta: ${maxAvailableDelta}`);
  876. const minAbsDelta = Math.min(Math.abs(delta), Math.abs(maxAvailableDelta));
  877. delta = delta < 0 ? 0 - minAbsDelta : minAbsDelta;
  878. // DEBUG.push(` -> adjusted delta: ${delta}`);
  879. // DEBUG.push("");
  880. }
  881. {
  882. // Delta added to a panel needs to be subtracted from other panels (within the constraints that those panels allow).
  883. const pivotIndex = delta < 0 ? firstPivotIndex : secondPivotIndex;
  884. let index = pivotIndex;
  885. while (index >= 0 && index < panelConstraintsArray.length) {
  886. const deltaRemaining = Math.abs(delta) - Math.abs(deltaApplied);
  887. const prevSize = initialLayout[index];
  888. assert(prevSize != null, `Previous layout not found for panel index ${index}`);
  889. const unsafeSize = prevSize - deltaRemaining;
  890. const safeSize = resizePanel({
  891. panelConstraints: panelConstraintsArray,
  892. panelIndex: index,
  893. size: unsafeSize
  894. });
  895. if (!fuzzyNumbersEqual(prevSize, safeSize)) {
  896. deltaApplied += prevSize - safeSize;
  897. nextLayout[index] = safeSize;
  898. if (deltaApplied.toPrecision(3).localeCompare(Math.abs(delta).toPrecision(3), undefined, {
  899. numeric: true
  900. }) >= 0) {
  901. break;
  902. }
  903. }
  904. if (delta < 0) {
  905. index--;
  906. } else {
  907. index++;
  908. }
  909. }
  910. }
  911. // DEBUG.push(`after 1: ${nextLayout.join(", ")}`);
  912. // DEBUG.push(` deltaApplied: ${deltaApplied}`);
  913. // DEBUG.push("");
  914. // If we were unable to resize any of the panels panels, return the previous state.
  915. // This will essentially bailout and ignore e.g. drags past a panel's boundaries
  916. if (fuzzyLayoutsEqual(prevLayout, nextLayout)) {
  917. // DEBUG.push(`bailout to previous layout: ${prevLayout.join(", ")}`);
  918. // console.log(DEBUG.join("\n"));
  919. return prevLayout;
  920. }
  921. {
  922. // Now distribute the applied delta to the panels in the other direction
  923. const pivotIndex = delta < 0 ? secondPivotIndex : firstPivotIndex;
  924. const prevSize = initialLayout[pivotIndex];
  925. assert(prevSize != null, `Previous layout not found for panel index ${pivotIndex}`);
  926. const unsafeSize = prevSize + deltaApplied;
  927. const safeSize = resizePanel({
  928. panelConstraints: panelConstraintsArray,
  929. panelIndex: pivotIndex,
  930. size: unsafeSize
  931. });
  932. // Adjust the pivot panel before, but only by the amount that surrounding panels were able to shrink/contract.
  933. nextLayout[pivotIndex] = safeSize;
  934. // Edge case where expanding or contracting one panel caused another one to change collapsed state
  935. if (!fuzzyNumbersEqual(safeSize, unsafeSize)) {
  936. let deltaRemaining = unsafeSize - safeSize;
  937. const pivotIndex = delta < 0 ? secondPivotIndex : firstPivotIndex;
  938. let index = pivotIndex;
  939. while (index >= 0 && index < panelConstraintsArray.length) {
  940. const prevSize = nextLayout[index];
  941. assert(prevSize != null, `Previous layout not found for panel index ${index}`);
  942. const unsafeSize = prevSize + deltaRemaining;
  943. const safeSize = resizePanel({
  944. panelConstraints: panelConstraintsArray,
  945. panelIndex: index,
  946. size: unsafeSize
  947. });
  948. if (!fuzzyNumbersEqual(prevSize, safeSize)) {
  949. deltaRemaining -= safeSize - prevSize;
  950. nextLayout[index] = safeSize;
  951. }
  952. if (fuzzyNumbersEqual(deltaRemaining, 0)) {
  953. break;
  954. }
  955. if (delta > 0) {
  956. index--;
  957. } else {
  958. index++;
  959. }
  960. }
  961. }
  962. }
  963. // DEBUG.push(`after 2: ${nextLayout.join(", ")}`);
  964. // DEBUG.push(` deltaApplied: ${deltaApplied}`);
  965. // DEBUG.push("");
  966. const totalSize = nextLayout.reduce((total, size) => size + total, 0);
  967. // DEBUG.push(`total size: ${totalSize}`);
  968. // If our new layout doesn't add up to 100%, that means the requested delta can't be applied
  969. // In that case, fall back to our most recent valid layout
  970. if (!fuzzyNumbersEqual(totalSize, 100)) {
  971. // DEBUG.push(`bailout to previous layout: ${prevLayout.join(", ")}`);
  972. // console.log(DEBUG.join("\n"));
  973. return prevLayout;
  974. }
  975. // console.log(DEBUG.join("\n"));
  976. return nextLayout;
  977. }
  978. function calculateAriaValues({
  979. layout,
  980. panelsArray,
  981. pivotIndices
  982. }) {
  983. let currentMinSize = 0;
  984. let currentMaxSize = 100;
  985. let totalMinSize = 0;
  986. let totalMaxSize = 0;
  987. const firstIndex = pivotIndices[0];
  988. assert(firstIndex != null, "No pivot index found");
  989. // A panel's effective min/max sizes also need to account for other panel's sizes.
  990. panelsArray.forEach((panelData, index) => {
  991. const {
  992. constraints
  993. } = panelData;
  994. const {
  995. maxSize = 100,
  996. minSize = 0
  997. } = constraints;
  998. if (index === firstIndex) {
  999. currentMinSize = minSize;
  1000. currentMaxSize = maxSize;
  1001. } else {
  1002. totalMinSize += minSize;
  1003. totalMaxSize += maxSize;
  1004. }
  1005. });
  1006. const valueMax = Math.min(currentMaxSize, 100 - totalMinSize);
  1007. const valueMin = Math.max(currentMinSize, 100 - totalMaxSize);
  1008. const valueNow = layout[firstIndex];
  1009. return {
  1010. valueMax,
  1011. valueMin,
  1012. valueNow
  1013. };
  1014. }
  1015. function getResizeHandleElementsForGroup(groupId, scope = document) {
  1016. return Array.from(scope.querySelectorAll(`[${DATA_ATTRIBUTES.resizeHandleId}][data-panel-group-id="${groupId}"]`));
  1017. }
  1018. function getResizeHandleElementIndex(groupId, id, scope = document) {
  1019. const handles = getResizeHandleElementsForGroup(groupId, scope);
  1020. const index = handles.findIndex(handle => handle.getAttribute(DATA_ATTRIBUTES.resizeHandleId) === id);
  1021. return index !== null && index !== void 0 ? index : null;
  1022. }
  1023. function determinePivotIndices(groupId, dragHandleId, panelGroupElement) {
  1024. const index = getResizeHandleElementIndex(groupId, dragHandleId, panelGroupElement);
  1025. return index != null ? [index, index + 1] : [-1, -1];
  1026. }
  1027. function getPanelGroupElement(id, rootElement = document) {
  1028. var _dataset;
  1029. //If the root element is the PanelGroup
  1030. if (rootElement instanceof HTMLElement && (rootElement === null || rootElement === void 0 ? void 0 : (_dataset = rootElement.dataset) === null || _dataset === void 0 ? void 0 : _dataset.panelGroupId) == id) {
  1031. return rootElement;
  1032. }
  1033. //Else query children
  1034. const element = rootElement.querySelector(`[data-panel-group][data-panel-group-id="${id}"]`);
  1035. if (element) {
  1036. return element;
  1037. }
  1038. return null;
  1039. }
  1040. function getResizeHandleElement(id, scope = document) {
  1041. const element = scope.querySelector(`[${DATA_ATTRIBUTES.resizeHandleId}="${id}"]`);
  1042. if (element) {
  1043. return element;
  1044. }
  1045. return null;
  1046. }
  1047. function getResizeHandlePanelIds(groupId, handleId, panelsArray, scope = document) {
  1048. var _panelsArray$index$id, _panelsArray$index, _panelsArray$id, _panelsArray;
  1049. const handle = getResizeHandleElement(handleId, scope);
  1050. const handles = getResizeHandleElementsForGroup(groupId, scope);
  1051. const index = handle ? handles.indexOf(handle) : -1;
  1052. const idBefore = (_panelsArray$index$id = (_panelsArray$index = panelsArray[index]) === null || _panelsArray$index === void 0 ? void 0 : _panelsArray$index.id) !== null && _panelsArray$index$id !== void 0 ? _panelsArray$index$id : null;
  1053. const idAfter = (_panelsArray$id = (_panelsArray = panelsArray[index + 1]) === null || _panelsArray === void 0 ? void 0 : _panelsArray.id) !== null && _panelsArray$id !== void 0 ? _panelsArray$id : null;
  1054. return [idBefore, idAfter];
  1055. }
  1056. // https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/
  1057. function useWindowSplitterPanelGroupBehavior({
  1058. committedValuesRef,
  1059. eagerValuesRef,
  1060. groupId,
  1061. layout,
  1062. panelDataArray,
  1063. panelGroupElement,
  1064. setLayout
  1065. }) {
  1066. React.useRef({
  1067. didWarnAboutMissingResizeHandle: false
  1068. });
  1069. useIsomorphicLayoutEffect(() => {
  1070. if (!panelGroupElement) {
  1071. return;
  1072. }
  1073. const resizeHandleElements = getResizeHandleElementsForGroup(groupId, panelGroupElement);
  1074. for (let index = 0; index < panelDataArray.length - 1; index++) {
  1075. const {
  1076. valueMax,
  1077. valueMin,
  1078. valueNow
  1079. } = calculateAriaValues({
  1080. layout,
  1081. panelsArray: panelDataArray,
  1082. pivotIndices: [index, index + 1]
  1083. });
  1084. const resizeHandleElement = resizeHandleElements[index];
  1085. if (resizeHandleElement == null) ; else {
  1086. const panelData = panelDataArray[index];
  1087. assert(panelData, `No panel data found for index "${index}"`);
  1088. resizeHandleElement.setAttribute("aria-controls", panelData.id);
  1089. resizeHandleElement.setAttribute("aria-valuemax", "" + Math.round(valueMax));
  1090. resizeHandleElement.setAttribute("aria-valuemin", "" + Math.round(valueMin));
  1091. resizeHandleElement.setAttribute("aria-valuenow", valueNow != null ? "" + Math.round(valueNow) : "");
  1092. }
  1093. }
  1094. return () => {
  1095. resizeHandleElements.forEach((resizeHandleElement, index) => {
  1096. resizeHandleElement.removeAttribute("aria-controls");
  1097. resizeHandleElement.removeAttribute("aria-valuemax");
  1098. resizeHandleElement.removeAttribute("aria-valuemin");
  1099. resizeHandleElement.removeAttribute("aria-valuenow");
  1100. });
  1101. };
  1102. }, [groupId, layout, panelDataArray, panelGroupElement]);
  1103. React.useEffect(() => {
  1104. if (!panelGroupElement) {
  1105. return;
  1106. }
  1107. const eagerValues = eagerValuesRef.current;
  1108. assert(eagerValues, `Eager values not found`);
  1109. const {
  1110. panelDataArray
  1111. } = eagerValues;
  1112. const groupElement = getPanelGroupElement(groupId, panelGroupElement);
  1113. assert(groupElement != null, `No group found for id "${groupId}"`);
  1114. const handles = getResizeHandleElementsForGroup(groupId, panelGroupElement);
  1115. assert(handles, `No resize handles found for group id "${groupId}"`);
  1116. const cleanupFunctions = handles.map(handle => {
  1117. const handleId = handle.getAttribute(DATA_ATTRIBUTES.resizeHandleId);
  1118. assert(handleId, `Resize handle element has no handle id attribute`);
  1119. const [idBefore, idAfter] = getResizeHandlePanelIds(groupId, handleId, panelDataArray, panelGroupElement);
  1120. if (idBefore == null || idAfter == null) {
  1121. return () => {};
  1122. }
  1123. const onKeyDown = event => {
  1124. if (event.defaultPrevented) {
  1125. return;
  1126. }
  1127. switch (event.key) {
  1128. case "Enter":
  1129. {
  1130. event.preventDefault();
  1131. const index = panelDataArray.findIndex(panelData => panelData.id === idBefore);
  1132. if (index >= 0) {
  1133. const panelData = panelDataArray[index];
  1134. assert(panelData, `No panel data found for index ${index}`);
  1135. const size = layout[index];
  1136. const {
  1137. collapsedSize = 0,
  1138. collapsible,
  1139. minSize = 0
  1140. } = panelData.constraints;
  1141. if (size != null && collapsible) {
  1142. const nextLayout = adjustLayoutByDelta({
  1143. delta: fuzzyNumbersEqual(size, collapsedSize) ? minSize - collapsedSize : collapsedSize - size,
  1144. initialLayout: layout,
  1145. panelConstraints: panelDataArray.map(panelData => panelData.constraints),
  1146. pivotIndices: determinePivotIndices(groupId, handleId, panelGroupElement),
  1147. prevLayout: layout,
  1148. trigger: "keyboard"
  1149. });
  1150. if (layout !== nextLayout) {
  1151. setLayout(nextLayout);
  1152. }
  1153. }
  1154. }
  1155. break;
  1156. }
  1157. }
  1158. };
  1159. handle.addEventListener("keydown", onKeyDown);
  1160. return () => {
  1161. handle.removeEventListener("keydown", onKeyDown);
  1162. };
  1163. });
  1164. return () => {
  1165. cleanupFunctions.forEach(cleanupFunction => cleanupFunction());
  1166. };
  1167. }, [panelGroupElement, committedValuesRef, eagerValuesRef, groupId, layout, panelDataArray, setLayout]);
  1168. }
  1169. function areEqual(arrayA, arrayB) {
  1170. if (arrayA.length !== arrayB.length) {
  1171. return false;
  1172. }
  1173. for (let index = 0; index < arrayA.length; index++) {
  1174. if (arrayA[index] !== arrayB[index]) {
  1175. return false;
  1176. }
  1177. }
  1178. return true;
  1179. }
  1180. function getResizeEventCursorPosition(direction, event) {
  1181. const isHorizontal = direction === "horizontal";
  1182. const {
  1183. x,
  1184. y
  1185. } = getResizeEventCoordinates(event);
  1186. return isHorizontal ? x : y;
  1187. }
  1188. function calculateDragOffsetPercentage(event, dragHandleId, direction, initialDragState, panelGroupElement) {
  1189. const isHorizontal = direction === "horizontal";
  1190. const handleElement = getResizeHandleElement(dragHandleId, panelGroupElement);
  1191. assert(handleElement, `No resize handle element found for id "${dragHandleId}"`);
  1192. const groupId = handleElement.getAttribute(DATA_ATTRIBUTES.groupId);
  1193. assert(groupId, `Resize handle element has no group id attribute`);
  1194. let {
  1195. initialCursorPosition
  1196. } = initialDragState;
  1197. const cursorPosition = getResizeEventCursorPosition(direction, event);
  1198. const groupElement = getPanelGroupElement(groupId, panelGroupElement);
  1199. assert(groupElement, `No group element found for id "${groupId}"`);
  1200. const groupRect = groupElement.getBoundingClientRect();
  1201. const groupSizeInPixels = isHorizontal ? groupRect.width : groupRect.height;
  1202. const offsetPixels = cursorPosition - initialCursorPosition;
  1203. const offsetPercentage = offsetPixels / groupSizeInPixels * 100;
  1204. return offsetPercentage;
  1205. }
  1206. // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX
  1207. function calculateDeltaPercentage(event, dragHandleId, direction, initialDragState, keyboardResizeBy, panelGroupElement) {
  1208. if (isKeyDown(event)) {
  1209. const isHorizontal = direction === "horizontal";
  1210. let delta = 0;
  1211. if (event.shiftKey) {
  1212. delta = 100;
  1213. } else if (keyboardResizeBy != null) {
  1214. delta = keyboardResizeBy;
  1215. } else {
  1216. delta = 10;
  1217. }
  1218. let movement = 0;
  1219. switch (event.key) {
  1220. case "ArrowDown":
  1221. movement = isHorizontal ? 0 : delta;
  1222. break;
  1223. case "ArrowLeft":
  1224. movement = isHorizontal ? -delta : 0;
  1225. break;
  1226. case "ArrowRight":
  1227. movement = isHorizontal ? delta : 0;
  1228. break;
  1229. case "ArrowUp":
  1230. movement = isHorizontal ? 0 : -delta;
  1231. break;
  1232. case "End":
  1233. movement = 100;
  1234. break;
  1235. case "Home":
  1236. movement = -100;
  1237. break;
  1238. }
  1239. return movement;
  1240. } else {
  1241. if (initialDragState == null) {
  1242. return 0;
  1243. }
  1244. return calculateDragOffsetPercentage(event, dragHandleId, direction, initialDragState, panelGroupElement);
  1245. }
  1246. }
  1247. function calculateUnsafeDefaultLayout({
  1248. panelDataArray
  1249. }) {
  1250. const layout = Array(panelDataArray.length);
  1251. const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
  1252. let numPanelsWithSizes = 0;
  1253. let remainingSize = 100;
  1254. // Distribute default sizes first
  1255. for (let index = 0; index < panelDataArray.length; index++) {
  1256. const panelConstraints = panelConstraintsArray[index];
  1257. assert(panelConstraints, `Panel constraints not found for index ${index}`);
  1258. const {
  1259. defaultSize
  1260. } = panelConstraints;
  1261. if (defaultSize != null) {
  1262. numPanelsWithSizes++;
  1263. layout[index] = defaultSize;
  1264. remainingSize -= defaultSize;
  1265. }
  1266. }
  1267. // Remaining size should be distributed evenly between panels without default sizes
  1268. for (let index = 0; index < panelDataArray.length; index++) {
  1269. const panelConstraints = panelConstraintsArray[index];
  1270. assert(panelConstraints, `Panel constraints not found for index ${index}`);
  1271. const {
  1272. defaultSize
  1273. } = panelConstraints;
  1274. if (defaultSize != null) {
  1275. continue;
  1276. }
  1277. const numRemainingPanels = panelDataArray.length - numPanelsWithSizes;
  1278. const size = remainingSize / numRemainingPanels;
  1279. numPanelsWithSizes++;
  1280. layout[index] = size;
  1281. remainingSize -= size;
  1282. }
  1283. return layout;
  1284. }
  1285. // Layout should be pre-converted into percentages
  1286. function callPanelCallbacks(panelsArray, layout, panelIdToLastNotifiedSizeMap) {
  1287. layout.forEach((size, index) => {
  1288. const panelData = panelsArray[index];
  1289. assert(panelData, `Panel data not found for index ${index}`);
  1290. const {
  1291. callbacks,
  1292. constraints,
  1293. id: panelId
  1294. } = panelData;
  1295. const {
  1296. collapsedSize = 0,
  1297. collapsible
  1298. } = constraints;
  1299. const lastNotifiedSize = panelIdToLastNotifiedSizeMap[panelId];
  1300. if (lastNotifiedSize == null || size !== lastNotifiedSize) {
  1301. panelIdToLastNotifiedSizeMap[panelId] = size;
  1302. const {
  1303. onCollapse,
  1304. onExpand,
  1305. onResize
  1306. } = callbacks;
  1307. if (onResize) {
  1308. onResize(size, lastNotifiedSize);
  1309. }
  1310. if (collapsible && (onCollapse || onExpand)) {
  1311. if (onExpand && (lastNotifiedSize == null || fuzzyNumbersEqual$1(lastNotifiedSize, collapsedSize)) && !fuzzyNumbersEqual$1(size, collapsedSize)) {
  1312. onExpand();
  1313. }
  1314. if (onCollapse && (lastNotifiedSize == null || !fuzzyNumbersEqual$1(lastNotifiedSize, collapsedSize)) && fuzzyNumbersEqual$1(size, collapsedSize)) {
  1315. onCollapse();
  1316. }
  1317. }
  1318. }
  1319. });
  1320. }
  1321. function compareLayouts(a, b) {
  1322. if (a.length !== b.length) {
  1323. return false;
  1324. } else {
  1325. for (let index = 0; index < a.length; index++) {
  1326. if (a[index] != b[index]) {
  1327. return false;
  1328. }
  1329. }
  1330. }
  1331. return true;
  1332. }
  1333. // This method returns a number between 1 and 100 representing
  1334. // the % of the group's overall space this panel should occupy.
  1335. function computePanelFlexBoxStyle({
  1336. defaultSize,
  1337. dragState,
  1338. layout,
  1339. panelData,
  1340. panelIndex,
  1341. precision = 3
  1342. }) {
  1343. const size = layout[panelIndex];
  1344. let flexGrow;
  1345. if (size == null) {
  1346. // Initial render (before panels have registered themselves)
  1347. // In order to support server rendering, fall back to default size if provided
  1348. flexGrow = defaultSize != undefined ? defaultSize.toPrecision(precision) : "1";
  1349. } else if (panelData.length === 1) {
  1350. // Special case: Single panel group should always fill full width/height
  1351. flexGrow = "1";
  1352. } else {
  1353. flexGrow = size.toPrecision(precision);
  1354. }
  1355. return {
  1356. flexBasis: 0,
  1357. flexGrow,
  1358. flexShrink: 1,
  1359. // Without this, Panel sizes may be unintentionally overridden by their content
  1360. overflow: "hidden",
  1361. // Disable pointer events inside of a panel during resize
  1362. // This avoid edge cases like nested iframes
  1363. pointerEvents: dragState !== null ? "none" : undefined
  1364. };
  1365. }
  1366. function debounce(callback, durationMs = 10) {
  1367. let timeoutId = null;
  1368. let callable = (...args) => {
  1369. if (timeoutId !== null) {
  1370. clearTimeout(timeoutId);
  1371. }
  1372. timeoutId = setTimeout(() => {
  1373. callback(...args);
  1374. }, durationMs);
  1375. };
  1376. return callable;
  1377. }
  1378. // PanelGroup might be rendering in a server-side environment where localStorage is not available
  1379. // or on a browser with cookies/storage disabled.
  1380. // In either case, this function avoids accessing localStorage until needed,
  1381. // and avoids throwing user-visible errors.
  1382. function initializeDefaultStorage(storageObject) {
  1383. try {
  1384. if (typeof localStorage !== "undefined") {
  1385. // Bypass this check for future calls
  1386. storageObject.getItem = name => {
  1387. return localStorage.getItem(name);
  1388. };
  1389. storageObject.setItem = (name, value) => {
  1390. localStorage.setItem(name, value);
  1391. };
  1392. } else {
  1393. throw new Error("localStorage not supported in this environment");
  1394. }
  1395. } catch (error) {
  1396. console.error(error);
  1397. storageObject.getItem = () => null;
  1398. storageObject.setItem = () => {};
  1399. }
  1400. }
  1401. function getPanelGroupKey(autoSaveId) {
  1402. return `react-resizable-panels:${autoSaveId}`;
  1403. }
  1404. // Note that Panel ids might be user-provided (stable) or useId generated (non-deterministic)
  1405. // so they should not be used as part of the serialization key.
  1406. // Using the min/max size attributes should work well enough as a backup.
  1407. // Pre-sorting by minSize allows remembering layouts even if panels are re-ordered/dragged.
  1408. function getPanelKey(panels) {
  1409. return panels.map(panel => {
  1410. const {
  1411. constraints,
  1412. id,
  1413. idIsFromProps,
  1414. order
  1415. } = panel;
  1416. if (idIsFromProps) {
  1417. return id;
  1418. } else {
  1419. return order ? `${order}:${JSON.stringify(constraints)}` : JSON.stringify(constraints);
  1420. }
  1421. }).sort((a, b) => a.localeCompare(b)).join(",");
  1422. }
  1423. function loadSerializedPanelGroupState(autoSaveId, storage) {
  1424. try {
  1425. const panelGroupKey = getPanelGroupKey(autoSaveId);
  1426. const serialized = storage.getItem(panelGroupKey);
  1427. if (serialized) {
  1428. const parsed = JSON.parse(serialized);
  1429. if (typeof parsed === "object" && parsed != null) {
  1430. return parsed;
  1431. }
  1432. }
  1433. } catch (error) {}
  1434. return null;
  1435. }
  1436. function loadPanelGroupState(autoSaveId, panels, storage) {
  1437. var _loadSerializedPanelG, _state$panelKey;
  1438. const state = (_loadSerializedPanelG = loadSerializedPanelGroupState(autoSaveId, storage)) !== null && _loadSerializedPanelG !== void 0 ? _loadSerializedPanelG : {};
  1439. const panelKey = getPanelKey(panels);
  1440. return (_state$panelKey = state[panelKey]) !== null && _state$panelKey !== void 0 ? _state$panelKey : null;
  1441. }
  1442. function savePanelGroupState(autoSaveId, panels, panelSizesBeforeCollapse, sizes, storage) {
  1443. var _loadSerializedPanelG2;
  1444. const panelGroupKey = getPanelGroupKey(autoSaveId);
  1445. const panelKey = getPanelKey(panels);
  1446. const state = (_loadSerializedPanelG2 = loadSerializedPanelGroupState(autoSaveId, storage)) !== null && _loadSerializedPanelG2 !== void 0 ? _loadSerializedPanelG2 : {};
  1447. state[panelKey] = {
  1448. expandToSizes: Object.fromEntries(panelSizesBeforeCollapse.entries()),
  1449. layout: sizes
  1450. };
  1451. try {
  1452. storage.setItem(panelGroupKey, JSON.stringify(state));
  1453. } catch (error) {
  1454. console.error(error);
  1455. }
  1456. }
  1457. // All units must be in percentages; pixel values should be pre-converted
  1458. function validatePanelGroupLayout({
  1459. layout: prevLayout,
  1460. panelConstraints
  1461. }) {
  1462. const nextLayout = [...prevLayout];
  1463. const nextLayoutTotalSize = nextLayout.reduce((accumulated, current) => accumulated + current, 0);
  1464. // Validate layout expectations
  1465. if (nextLayout.length !== panelConstraints.length) {
  1466. throw Error(`Invalid ${panelConstraints.length} panel layout: ${nextLayout.map(size => `${size}%`).join(", ")}`);
  1467. } else if (!fuzzyNumbersEqual(nextLayoutTotalSize, 100) && nextLayout.length > 0) {
  1468. for (let index = 0; index < panelConstraints.length; index++) {
  1469. const unsafeSize = nextLayout[index];
  1470. assert(unsafeSize != null, `No layout data found for index ${index}`);
  1471. const safeSize = 100 / nextLayoutTotalSize * unsafeSize;
  1472. nextLayout[index] = safeSize;
  1473. }
  1474. }
  1475. let remainingSize = 0;
  1476. // First pass: Validate the proposed layout given each panel's constraints
  1477. for (let index = 0; index < panelConstraints.length; index++) {
  1478. const unsafeSize = nextLayout[index];
  1479. assert(unsafeSize != null, `No layout data found for index ${index}`);
  1480. const safeSize = resizePanel({
  1481. panelConstraints,
  1482. panelIndex: index,
  1483. size: unsafeSize
  1484. });
  1485. if (unsafeSize != safeSize) {
  1486. remainingSize += unsafeSize - safeSize;
  1487. nextLayout[index] = safeSize;
  1488. }
  1489. }
  1490. // If there is additional, left over space, assign it to any panel(s) that permits it
  1491. // (It's not worth taking multiple additional passes to evenly distribute)
  1492. if (!fuzzyNumbersEqual(remainingSize, 0)) {
  1493. for (let index = 0; index < panelConstraints.length; index++) {
  1494. const prevSize = nextLayout[index];
  1495. assert(prevSize != null, `No layout data found for index ${index}`);
  1496. const unsafeSize = prevSize + remainingSize;
  1497. const safeSize = resizePanel({
  1498. panelConstraints,
  1499. panelIndex: index,
  1500. size: unsafeSize
  1501. });
  1502. if (prevSize !== safeSize) {
  1503. remainingSize -= safeSize - prevSize;
  1504. nextLayout[index] = safeSize;
  1505. // Once we've used up the remainder, bail
  1506. if (fuzzyNumbersEqual(remainingSize, 0)) {
  1507. break;
  1508. }
  1509. }
  1510. }
  1511. }
  1512. return nextLayout;
  1513. }
  1514. const LOCAL_STORAGE_DEBOUNCE_INTERVAL = 100;
  1515. const defaultStorage = {
  1516. getItem: name => {
  1517. initializeDefaultStorage(defaultStorage);
  1518. return defaultStorage.getItem(name);
  1519. },
  1520. setItem: (name, value) => {
  1521. initializeDefaultStorage(defaultStorage);
  1522. defaultStorage.setItem(name, value);
  1523. }
  1524. };
  1525. const debounceMap = {};
  1526. function PanelGroupWithForwardedRef({
  1527. autoSaveId = null,
  1528. children,
  1529. className: classNameFromProps = "",
  1530. direction,
  1531. forwardedRef,
  1532. id: idFromProps = null,
  1533. onLayout = null,
  1534. keyboardResizeBy = null,
  1535. storage = defaultStorage,
  1536. style: styleFromProps,
  1537. tagName: Type = "div",
  1538. ...rest
  1539. }) {
  1540. const groupId = useUniqueId(idFromProps);
  1541. const panelGroupElementRef = React.useRef(null);
  1542. const [dragState, setDragState] = React.useState(null);
  1543. const [layout, setLayout] = React.useState([]);
  1544. const forceUpdate = useForceUpdate();
  1545. const panelIdToLastNotifiedSizeMapRef = React.useRef({});
  1546. const panelSizeBeforeCollapseRef = React.useRef(new Map());
  1547. const prevDeltaRef = React.useRef(0);
  1548. const committedValuesRef = React.useRef({
  1549. autoSaveId,
  1550. direction,
  1551. dragState,
  1552. id: groupId,
  1553. keyboardResizeBy,
  1554. onLayout,
  1555. storage
  1556. });
  1557. const eagerValuesRef = React.useRef({
  1558. layout,
  1559. panelDataArray: [],
  1560. panelDataArrayChanged: false
  1561. });
  1562. React.useRef({
  1563. didLogIdAndOrderWarning: false,
  1564. didLogPanelConstraintsWarning: false,
  1565. prevPanelIds: []
  1566. });
  1567. React.useImperativeHandle(forwardedRef, () => ({
  1568. getId: () => committedValuesRef.current.id,
  1569. getLayout: () => {
  1570. const {
  1571. layout
  1572. } = eagerValuesRef.current;
  1573. return layout;
  1574. },
  1575. setLayout: unsafeLayout => {
  1576. const {
  1577. onLayout
  1578. } = committedValuesRef.current;
  1579. const {
  1580. layout: prevLayout,
  1581. panelDataArray
  1582. } = eagerValuesRef.current;
  1583. const safeLayout = validatePanelGroupLayout({
  1584. layout: unsafeLayout,
  1585. panelConstraints: panelDataArray.map(panelData => panelData.constraints)
  1586. });
  1587. if (!areEqual(prevLayout, safeLayout)) {
  1588. setLayout(safeLayout);
  1589. eagerValuesRef.current.layout = safeLayout;
  1590. if (onLayout) {
  1591. onLayout(safeLayout);
  1592. }
  1593. callPanelCallbacks(panelDataArray, safeLayout, panelIdToLastNotifiedSizeMapRef.current);
  1594. }
  1595. }
  1596. }), []);
  1597. useIsomorphicLayoutEffect(() => {
  1598. committedValuesRef.current.autoSaveId = autoSaveId;
  1599. committedValuesRef.current.direction = direction;
  1600. committedValuesRef.current.dragState = dragState;
  1601. committedValuesRef.current.id = groupId;
  1602. committedValuesRef.current.onLayout = onLayout;
  1603. committedValuesRef.current.storage = storage;
  1604. });
  1605. useWindowSplitterPanelGroupBehavior({
  1606. committedValuesRef,
  1607. eagerValuesRef,
  1608. groupId,
  1609. layout,
  1610. panelDataArray: eagerValuesRef.current.panelDataArray,
  1611. setLayout,
  1612. panelGroupElement: panelGroupElementRef.current
  1613. });
  1614. React.useEffect(() => {
  1615. const {
  1616. panelDataArray
  1617. } = eagerValuesRef.current;
  1618. // If this panel has been configured to persist sizing information, save sizes to local storage.
  1619. if (autoSaveId) {
  1620. if (layout.length === 0 || layout.length !== panelDataArray.length) {
  1621. return;
  1622. }
  1623. let debouncedSave = debounceMap[autoSaveId];
  1624. // Limit the frequency of localStorage updates.
  1625. if (debouncedSave == null) {
  1626. debouncedSave = debounce(savePanelGroupState, LOCAL_STORAGE_DEBOUNCE_INTERVAL);
  1627. debounceMap[autoSaveId] = debouncedSave;
  1628. }
  1629. // Clone mutable data before passing to the debounced function,
  1630. // else we run the risk of saving an incorrect combination of mutable and immutable values to state.
  1631. const clonedPanelDataArray = [...panelDataArray];
  1632. const clonedPanelSizesBeforeCollapse = new Map(panelSizeBeforeCollapseRef.current);
  1633. debouncedSave(autoSaveId, clonedPanelDataArray, clonedPanelSizesBeforeCollapse, layout, storage);
  1634. }
  1635. }, [autoSaveId, layout, storage]);
  1636. // DEV warnings
  1637. React.useEffect(() => {
  1638. });
  1639. // External APIs are safe to memoize via committed values ref
  1640. const collapsePanel = React.useCallback(panelData => {
  1641. const {
  1642. onLayout
  1643. } = committedValuesRef.current;
  1644. const {
  1645. layout: prevLayout,
  1646. panelDataArray
  1647. } = eagerValuesRef.current;
  1648. if (panelData.constraints.collapsible) {
  1649. const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
  1650. const {
  1651. collapsedSize = 0,
  1652. panelSize,
  1653. pivotIndices
  1654. } = panelDataHelper(panelDataArray, panelData, prevLayout);
  1655. assert(panelSize != null, `Panel size not found for panel "${panelData.id}"`);
  1656. if (!fuzzyNumbersEqual$1(panelSize, collapsedSize)) {
  1657. // Store size before collapse;
  1658. // This is the size that gets restored if the expand() API is used.
  1659. panelSizeBeforeCollapseRef.current.set(panelData.id, panelSize);
  1660. const isLastPanel = findPanelDataIndex(panelDataArray, panelData) === panelDataArray.length - 1;
  1661. const delta = isLastPanel ? panelSize - collapsedSize : collapsedSize - panelSize;
  1662. const nextLayout = adjustLayoutByDelta({
  1663. delta,
  1664. initialLayout: prevLayout,
  1665. panelConstraints: panelConstraintsArray,
  1666. pivotIndices,
  1667. prevLayout,
  1668. trigger: "imperative-api"
  1669. });
  1670. if (!compareLayouts(prevLayout, nextLayout)) {
  1671. setLayout(nextLayout);
  1672. eagerValuesRef.current.layout = nextLayout;
  1673. if (onLayout) {
  1674. onLayout(nextLayout);
  1675. }
  1676. callPanelCallbacks(panelDataArray, nextLayout, panelIdToLastNotifiedSizeMapRef.current);
  1677. }
  1678. }
  1679. }
  1680. }, []);
  1681. // External APIs are safe to memoize via committed values ref
  1682. const expandPanel = React.useCallback((panelData, minSizeOverride) => {
  1683. const {
  1684. onLayout
  1685. } = committedValuesRef.current;
  1686. const {
  1687. layout: prevLayout,
  1688. panelDataArray
  1689. } = eagerValuesRef.current;
  1690. if (panelData.constraints.collapsible) {
  1691. const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
  1692. const {
  1693. collapsedSize = 0,
  1694. panelSize = 0,
  1695. minSize: minSizeFromProps = 0,
  1696. pivotIndices
  1697. } = panelDataHelper(panelDataArray, panelData, prevLayout);
  1698. const minSize = minSizeOverride !== null && minSizeOverride !== void 0 ? minSizeOverride : minSizeFromProps;
  1699. if (fuzzyNumbersEqual$1(panelSize, collapsedSize)) {
  1700. // Restore this panel to the size it was before it was collapsed, if possible.
  1701. const prevPanelSize = panelSizeBeforeCollapseRef.current.get(panelData.id);
  1702. const baseSize = prevPanelSize != null && prevPanelSize >= minSize ? prevPanelSize : minSize;
  1703. const isLastPanel = findPanelDataIndex(panelDataArray, panelData) === panelDataArray.length - 1;
  1704. const delta = isLastPanel ? panelSize - baseSize : baseSize - panelSize;
  1705. const nextLayout = adjustLayoutByDelta({
  1706. delta,
  1707. initialLayout: prevLayout,
  1708. panelConstraints: panelConstraintsArray,
  1709. pivotIndices,
  1710. prevLayout,
  1711. trigger: "imperative-api"
  1712. });
  1713. if (!compareLayouts(prevLayout, nextLayout)) {
  1714. setLayout(nextLayout);
  1715. eagerValuesRef.current.layout = nextLayout;
  1716. if (onLayout) {
  1717. onLayout(nextLayout);
  1718. }
  1719. callPanelCallbacks(panelDataArray, nextLayout, panelIdToLastNotifiedSizeMapRef.current);
  1720. }
  1721. }
  1722. }
  1723. }, []);
  1724. // External APIs are safe to memoize via committed values ref
  1725. const getPanelSize = React.useCallback(panelData => {
  1726. const {
  1727. layout,
  1728. panelDataArray
  1729. } = eagerValuesRef.current;
  1730. const {
  1731. panelSize
  1732. } = panelDataHelper(panelDataArray, panelData, layout);
  1733. assert(panelSize != null, `Panel size not found for panel "${panelData.id}"`);
  1734. return panelSize;
  1735. }, []);
  1736. // This API should never read from committedValuesRef
  1737. const getPanelStyle = React.useCallback((panelData, defaultSize) => {
  1738. const {
  1739. panelDataArray
  1740. } = eagerValuesRef.current;
  1741. const panelIndex = findPanelDataIndex(panelDataArray, panelData);
  1742. return computePanelFlexBoxStyle({
  1743. defaultSize,
  1744. dragState,
  1745. layout,
  1746. panelData: panelDataArray,
  1747. panelIndex
  1748. });
  1749. }, [dragState, layout]);
  1750. // External APIs are safe to memoize via committed values ref
  1751. const isPanelCollapsed = React.useCallback(panelData => {
  1752. const {
  1753. layout,
  1754. panelDataArray
  1755. } = eagerValuesRef.current;
  1756. const {
  1757. collapsedSize = 0,
  1758. collapsible,
  1759. panelSize
  1760. } = panelDataHelper(panelDataArray, panelData, layout);
  1761. assert(panelSize != null, `Panel size not found for panel "${panelData.id}"`);
  1762. return collapsible === true && fuzzyNumbersEqual$1(panelSize, collapsedSize);
  1763. }, []);
  1764. // External APIs are safe to memoize via committed values ref
  1765. const isPanelExpanded = React.useCallback(panelData => {
  1766. const {
  1767. layout,
  1768. panelDataArray
  1769. } = eagerValuesRef.current;
  1770. const {
  1771. collapsedSize = 0,
  1772. collapsible,
  1773. panelSize
  1774. } = panelDataHelper(panelDataArray, panelData, layout);
  1775. assert(panelSize != null, `Panel size not found for panel "${panelData.id}"`);
  1776. return !collapsible || fuzzyCompareNumbers(panelSize, collapsedSize) > 0;
  1777. }, []);
  1778. const registerPanel = React.useCallback(panelData => {
  1779. const {
  1780. panelDataArray
  1781. } = eagerValuesRef.current;
  1782. panelDataArray.push(panelData);
  1783. panelDataArray.sort((panelA, panelB) => {
  1784. const orderA = panelA.order;
  1785. const orderB = panelB.order;
  1786. if (orderA == null && orderB == null) {
  1787. return 0;
  1788. } else if (orderA == null) {
  1789. return -1;
  1790. } else if (orderB == null) {
  1791. return 1;
  1792. } else {
  1793. return orderA - orderB;
  1794. }
  1795. });
  1796. eagerValuesRef.current.panelDataArrayChanged = true;
  1797. forceUpdate();
  1798. }, [forceUpdate]);
  1799. // (Re)calculate group layout whenever panels are registered or unregistered.
  1800. // eslint-disable-next-line react-hooks/exhaustive-deps
  1801. useIsomorphicLayoutEffect(() => {
  1802. if (eagerValuesRef.current.panelDataArrayChanged) {
  1803. eagerValuesRef.current.panelDataArrayChanged = false;
  1804. const {
  1805. autoSaveId,
  1806. onLayout,
  1807. storage
  1808. } = committedValuesRef.current;
  1809. const {
  1810. layout: prevLayout,
  1811. panelDataArray
  1812. } = eagerValuesRef.current;
  1813. // If this panel has been configured to persist sizing information,
  1814. // default size should be restored from local storage if possible.
  1815. let unsafeLayout = null;
  1816. if (autoSaveId) {
  1817. const state = loadPanelGroupState(autoSaveId, panelDataArray, storage);
  1818. if (state) {
  1819. panelSizeBeforeCollapseRef.current = new Map(Object.entries(state.expandToSizes));
  1820. unsafeLayout = state.layout;
  1821. }
  1822. }
  1823. if (unsafeLayout == null) {
  1824. unsafeLayout = calculateUnsafeDefaultLayout({
  1825. panelDataArray
  1826. });
  1827. }
  1828. // Validate even saved layouts in case something has changed since last render
  1829. // e.g. for pixel groups, this could be the size of the window
  1830. const nextLayout = validatePanelGroupLayout({
  1831. layout: unsafeLayout,
  1832. panelConstraints: panelDataArray.map(panelData => panelData.constraints)
  1833. });
  1834. if (!areEqual(prevLayout, nextLayout)) {
  1835. setLayout(nextLayout);
  1836. eagerValuesRef.current.layout = nextLayout;
  1837. if (onLayout) {
  1838. onLayout(nextLayout);
  1839. }
  1840. callPanelCallbacks(panelDataArray, nextLayout, panelIdToLastNotifiedSizeMapRef.current);
  1841. }
  1842. }
  1843. });
  1844. // Reset the cached layout if hidden by the Activity/Offscreen API
  1845. useIsomorphicLayoutEffect(() => {
  1846. const eagerValues = eagerValuesRef.current;
  1847. return () => {
  1848. eagerValues.layout = [];
  1849. };
  1850. }, []);
  1851. const registerResizeHandle = React.useCallback(dragHandleId => {
  1852. let isRTL = false;
  1853. const panelGroupElement = panelGroupElementRef.current;
  1854. if (panelGroupElement) {
  1855. const style = window.getComputedStyle(panelGroupElement, null);
  1856. if (style.getPropertyValue("direction") === "rtl") {
  1857. isRTL = true;
  1858. }
  1859. }
  1860. return function resizeHandler(event) {
  1861. event.preventDefault();
  1862. const panelGroupElement = panelGroupElementRef.current;
  1863. if (!panelGroupElement) {
  1864. return () => null;
  1865. }
  1866. const {
  1867. direction,
  1868. dragState,
  1869. id: groupId,
  1870. keyboardResizeBy,
  1871. onLayout
  1872. } = committedValuesRef.current;
  1873. const {
  1874. layout: prevLayout,
  1875. panelDataArray
  1876. } = eagerValuesRef.current;
  1877. const {
  1878. initialLayout
  1879. } = dragState !== null && dragState !== void 0 ? dragState : {};
  1880. const pivotIndices = determinePivotIndices(groupId, dragHandleId, panelGroupElement);
  1881. let delta = calculateDeltaPercentage(event, dragHandleId, direction, dragState, keyboardResizeBy, panelGroupElement);
  1882. const isHorizontal = direction === "horizontal";
  1883. if (isHorizontal && isRTL) {
  1884. delta = -delta;
  1885. }
  1886. const panelConstraints = panelDataArray.map(panelData => panelData.constraints);
  1887. const nextLayout = adjustLayoutByDelta({
  1888. delta,
  1889. initialLayout: initialLayout !== null && initialLayout !== void 0 ? initialLayout : prevLayout,
  1890. panelConstraints,
  1891. pivotIndices,
  1892. prevLayout,
  1893. trigger: isKeyDown(event) ? "keyboard" : "mouse-or-touch"
  1894. });
  1895. const layoutChanged = !compareLayouts(prevLayout, nextLayout);
  1896. // Only update the cursor for layout changes triggered by touch/mouse events (not keyboard)
  1897. // Update the cursor even if the layout hasn't changed (we may need to show an invalid cursor state)
  1898. if (isPointerEvent(event) || isMouseEvent(event)) {
  1899. // Watch for multiple subsequent deltas; this might occur for tiny cursor movements.
  1900. // In this case, Panel sizes might not change–
  1901. // but updating cursor in this scenario would cause a flicker.
  1902. if (prevDeltaRef.current != delta) {
  1903. prevDeltaRef.current = delta;
  1904. if (!layoutChanged && delta !== 0) {
  1905. // If the pointer has moved too far to resize the panel any further, note this so we can update the cursor.
  1906. // This mimics VS Code behavior.
  1907. if (isHorizontal) {
  1908. reportConstraintsViolation(dragHandleId, delta < 0 ? EXCEEDED_HORIZONTAL_MIN : EXCEEDED_HORIZONTAL_MAX);
  1909. } else {
  1910. reportConstraintsViolation(dragHandleId, delta < 0 ? EXCEEDED_VERTICAL_MIN : EXCEEDED_VERTICAL_MAX);
  1911. }
  1912. } else {
  1913. reportConstraintsViolation(dragHandleId, 0);
  1914. }
  1915. }
  1916. }
  1917. if (layoutChanged) {
  1918. setLayout(nextLayout);
  1919. eagerValuesRef.current.layout = nextLayout;
  1920. if (onLayout) {
  1921. onLayout(nextLayout);
  1922. }
  1923. callPanelCallbacks(panelDataArray, nextLayout, panelIdToLastNotifiedSizeMapRef.current);
  1924. }
  1925. };
  1926. }, []);
  1927. // External APIs are safe to memoize via committed values ref
  1928. const resizePanel = React.useCallback((panelData, unsafePanelSize) => {
  1929. const {
  1930. onLayout
  1931. } = committedValuesRef.current;
  1932. const {
  1933. layout: prevLayout,
  1934. panelDataArray
  1935. } = eagerValuesRef.current;
  1936. const panelConstraintsArray = panelDataArray.map(panelData => panelData.constraints);
  1937. const {
  1938. panelSize,
  1939. pivotIndices
  1940. } = panelDataHelper(panelDataArray, panelData, prevLayout);
  1941. assert(panelSize != null, `Panel size not found for panel "${panelData.id}"`);
  1942. const isLastPanel = findPanelDataIndex(panelDataArray, panelData) === panelDataArray.length - 1;
  1943. const delta = isLastPanel ? panelSize - unsafePanelSize : unsafePanelSize - panelSize;
  1944. const nextLayout = adjustLayoutByDelta({
  1945. delta,
  1946. initialLayout: prevLayout,
  1947. panelConstraints: panelConstraintsArray,
  1948. pivotIndices,
  1949. prevLayout,
  1950. trigger: "imperative-api"
  1951. });
  1952. if (!compareLayouts(prevLayout, nextLayout)) {
  1953. setLayout(nextLayout);
  1954. eagerValuesRef.current.layout = nextLayout;
  1955. if (onLayout) {
  1956. onLayout(nextLayout);
  1957. }
  1958. callPanelCallbacks(panelDataArray, nextLayout, panelIdToLastNotifiedSizeMapRef.current);
  1959. }
  1960. }, []);
  1961. const reevaluatePanelConstraints = React.useCallback((panelData, prevConstraints) => {
  1962. const {
  1963. layout,
  1964. panelDataArray
  1965. } = eagerValuesRef.current;
  1966. const {
  1967. collapsedSize: prevCollapsedSize = 0,
  1968. collapsible: prevCollapsible
  1969. } = prevConstraints;
  1970. const {
  1971. collapsedSize: nextCollapsedSize = 0,
  1972. collapsible: nextCollapsible,
  1973. maxSize: nextMaxSize = 100,
  1974. minSize: nextMinSize = 0
  1975. } = panelData.constraints;
  1976. const {
  1977. panelSize: prevPanelSize
  1978. } = panelDataHelper(panelDataArray, panelData, layout);
  1979. if (prevPanelSize == null) {
  1980. // It's possible that the panels in this group have changed since the last render
  1981. return;
  1982. }
  1983. if (prevCollapsible && nextCollapsible && fuzzyNumbersEqual$1(prevPanelSize, prevCollapsedSize)) {
  1984. if (!fuzzyNumbersEqual$1(prevCollapsedSize, nextCollapsedSize)) {
  1985. resizePanel(panelData, nextCollapsedSize);
  1986. }
  1987. } else if (prevPanelSize < nextMinSize) {
  1988. resizePanel(panelData, nextMinSize);
  1989. } else if (prevPanelSize > nextMaxSize) {
  1990. resizePanel(panelData, nextMaxSize);
  1991. }
  1992. }, [resizePanel]);
  1993. // TODO Multiple drag handles can be active at the same time so this API is a bit awkward now
  1994. const startDragging = React.useCallback((dragHandleId, event) => {
  1995. const {
  1996. direction
  1997. } = committedValuesRef.current;
  1998. const {
  1999. layout
  2000. } = eagerValuesRef.current;
  2001. if (!panelGroupElementRef.current) {
  2002. return;
  2003. }
  2004. const handleElement = getResizeHandleElement(dragHandleId, panelGroupElementRef.current);
  2005. assert(handleElement, `Drag handle element not found for id "${dragHandleId}"`);
  2006. const initialCursorPosition = getResizeEventCursorPosition(direction, event);
  2007. setDragState({
  2008. dragHandleId,
  2009. dragHandleRect: handleElement.getBoundingClientRect(),
  2010. initialCursorPosition,
  2011. initialLayout: layout
  2012. });
  2013. }, []);
  2014. const stopDragging = React.useCallback(() => {
  2015. setDragState(null);
  2016. }, []);
  2017. const unregisterPanel = React.useCallback(panelData => {
  2018. const {
  2019. panelDataArray
  2020. } = eagerValuesRef.current;
  2021. const index = findPanelDataIndex(panelDataArray, panelData);
  2022. if (index >= 0) {
  2023. panelDataArray.splice(index, 1);
  2024. // TRICKY
  2025. // When a panel is removed from the group, we should delete the most recent prev-size entry for it.
  2026. // If we don't do this, then a conditionally rendered panel might not call onResize when it's re-mounted.
  2027. // Strict effects mode makes this tricky though because all panels will be registered, unregistered, then re-registered on mount.
  2028. delete panelIdToLastNotifiedSizeMapRef.current[panelData.id];
  2029. eagerValuesRef.current.panelDataArrayChanged = true;
  2030. forceUpdate();
  2031. }
  2032. }, [forceUpdate]);
  2033. const context = React.useMemo(() => ({
  2034. collapsePanel,
  2035. direction,
  2036. dragState,
  2037. expandPanel,
  2038. getPanelSize,
  2039. getPanelStyle,
  2040. groupId,
  2041. isPanelCollapsed,
  2042. isPanelExpanded,
  2043. reevaluatePanelConstraints,
  2044. registerPanel,
  2045. registerResizeHandle,
  2046. resizePanel,
  2047. startDragging,
  2048. stopDragging,
  2049. unregisterPanel,
  2050. panelGroupElement: panelGroupElementRef.current
  2051. }), [collapsePanel, dragState, direction, expandPanel, getPanelSize, getPanelStyle, groupId, isPanelCollapsed, isPanelExpanded, reevaluatePanelConstraints, registerPanel, registerResizeHandle, resizePanel, startDragging, stopDragging, unregisterPanel]);
  2052. const style = {
  2053. display: "flex",
  2054. flexDirection: direction === "horizontal" ? "row" : "column",
  2055. height: "100%",
  2056. overflow: "hidden",
  2057. width: "100%"
  2058. };
  2059. return React.createElement(PanelGroupContext.Provider, {
  2060. value: context
  2061. }, React.createElement(Type, {
  2062. ...rest,
  2063. children,
  2064. className: classNameFromProps,
  2065. id: idFromProps,
  2066. ref: panelGroupElementRef,
  2067. style: {
  2068. ...style,
  2069. ...styleFromProps
  2070. },
  2071. // CSS selectors
  2072. [DATA_ATTRIBUTES.group]: "",
  2073. [DATA_ATTRIBUTES.groupDirection]: direction,
  2074. [DATA_ATTRIBUTES.groupId]: groupId
  2075. }));
  2076. }
  2077. const PanelGroup = React.forwardRef((props, ref) => React.createElement(PanelGroupWithForwardedRef, {
  2078. ...props,
  2079. forwardedRef: ref
  2080. }));
  2081. PanelGroupWithForwardedRef.displayName = "PanelGroup";
  2082. PanelGroup.displayName = "forwardRef(PanelGroup)";
  2083. function findPanelDataIndex(panelDataArray, panelData) {
  2084. return panelDataArray.findIndex(prevPanelData => prevPanelData === panelData || prevPanelData.id === panelData.id);
  2085. }
  2086. function panelDataHelper(panelDataArray, panelData, layout) {
  2087. const panelIndex = findPanelDataIndex(panelDataArray, panelData);
  2088. const isLastPanel = panelIndex === panelDataArray.length - 1;
  2089. const pivotIndices = isLastPanel ? [panelIndex - 1, panelIndex] : [panelIndex, panelIndex + 1];
  2090. const panelSize = layout[panelIndex];
  2091. return {
  2092. ...panelData.constraints,
  2093. panelSize,
  2094. pivotIndices
  2095. };
  2096. }
  2097. // https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/
  2098. function useWindowSplitterResizeHandlerBehavior({
  2099. disabled,
  2100. handleId,
  2101. resizeHandler,
  2102. panelGroupElement
  2103. }) {
  2104. React.useEffect(() => {
  2105. if (disabled || resizeHandler == null || panelGroupElement == null) {
  2106. return;
  2107. }
  2108. const handleElement = getResizeHandleElement(handleId, panelGroupElement);
  2109. if (handleElement == null) {
  2110. return;
  2111. }
  2112. const onKeyDown = event => {
  2113. if (event.defaultPrevented) {
  2114. return;
  2115. }
  2116. switch (event.key) {
  2117. case "ArrowDown":
  2118. case "ArrowLeft":
  2119. case "ArrowRight":
  2120. case "ArrowUp":
  2121. case "End":
  2122. case "Home":
  2123. {
  2124. event.preventDefault();
  2125. resizeHandler(event);
  2126. break;
  2127. }
  2128. case "F6":
  2129. {
  2130. event.preventDefault();
  2131. const groupId = handleElement.getAttribute(DATA_ATTRIBUTES.groupId);
  2132. assert(groupId, `No group element found for id "${groupId}"`);
  2133. const handles = getResizeHandleElementsForGroup(groupId, panelGroupElement);
  2134. const index = getResizeHandleElementIndex(groupId, handleId, panelGroupElement);
  2135. assert(index !== null, `No resize element found for id "${handleId}"`);
  2136. const nextIndex = event.shiftKey ? index > 0 ? index - 1 : handles.length - 1 : index + 1 < handles.length ? index + 1 : 0;
  2137. const nextHandle = handles[nextIndex];
  2138. nextHandle.focus();
  2139. break;
  2140. }
  2141. }
  2142. };
  2143. handleElement.addEventListener("keydown", onKeyDown);
  2144. return () => {
  2145. handleElement.removeEventListener("keydown", onKeyDown);
  2146. };
  2147. }, [panelGroupElement, disabled, handleId, resizeHandler]);
  2148. }
  2149. function PanelResizeHandle({
  2150. children = null,
  2151. className: classNameFromProps = "",
  2152. disabled = false,
  2153. hitAreaMargins,
  2154. id: idFromProps,
  2155. onBlur,
  2156. onClick,
  2157. onDragging,
  2158. onFocus,
  2159. onPointerDown,
  2160. onPointerUp,
  2161. style: styleFromProps = {},
  2162. tabIndex = 0,
  2163. tagName: Type = "div",
  2164. ...rest
  2165. }) {
  2166. var _hitAreaMargins$coars, _hitAreaMargins$fine;
  2167. const elementRef = React.useRef(null);
  2168. // Use a ref to guard against users passing inline props
  2169. const callbacksRef = React.useRef({
  2170. onClick,
  2171. onDragging,
  2172. onPointerDown,
  2173. onPointerUp
  2174. });
  2175. React.useEffect(() => {
  2176. callbacksRef.current.onClick = onClick;
  2177. callbacksRef.current.onDragging = onDragging;
  2178. callbacksRef.current.onPointerDown = onPointerDown;
  2179. callbacksRef.current.onPointerUp = onPointerUp;
  2180. });
  2181. const panelGroupContext = React.useContext(PanelGroupContext);
  2182. if (panelGroupContext === null) {
  2183. throw Error(`PanelResizeHandle components must be rendered within a PanelGroup container`);
  2184. }
  2185. const {
  2186. direction,
  2187. groupId,
  2188. registerResizeHandle: registerResizeHandleWithParentGroup,
  2189. startDragging,
  2190. stopDragging,
  2191. panelGroupElement
  2192. } = panelGroupContext;
  2193. const resizeHandleId = useUniqueId(idFromProps);
  2194. const [state, setState] = React.useState("inactive");
  2195. const [isFocused, setIsFocused] = React.useState(false);
  2196. const [resizeHandler, setResizeHandler] = React.useState(null);
  2197. const committedValuesRef = React.useRef({
  2198. state
  2199. });
  2200. useIsomorphicLayoutEffect(() => {
  2201. committedValuesRef.current.state = state;
  2202. });
  2203. React.useEffect(() => {
  2204. if (disabled) {
  2205. setResizeHandler(null);
  2206. } else {
  2207. const resizeHandler = registerResizeHandleWithParentGroup(resizeHandleId);
  2208. setResizeHandler(() => resizeHandler);
  2209. }
  2210. }, [disabled, resizeHandleId, registerResizeHandleWithParentGroup]);
  2211. // Extract hit area margins before passing them to the effect's dependency array
  2212. // so that inline object values won't trigger re-renders
  2213. const coarseHitAreaMargins = (_hitAreaMargins$coars = hitAreaMargins === null || hitAreaMargins === void 0 ? void 0 : hitAreaMargins.coarse) !== null && _hitAreaMargins$coars !== void 0 ? _hitAreaMargins$coars : 15;
  2214. const fineHitAreaMargins = (_hitAreaMargins$fine = hitAreaMargins === null || hitAreaMargins === void 0 ? void 0 : hitAreaMargins.fine) !== null && _hitAreaMargins$fine !== void 0 ? _hitAreaMargins$fine : 5;
  2215. React.useEffect(() => {
  2216. if (disabled || resizeHandler == null) {
  2217. return;
  2218. }
  2219. const element = elementRef.current;
  2220. assert(element, "Element ref not attached");
  2221. let didMove = false;
  2222. const setResizeHandlerState = (action, isActive, event) => {
  2223. if (!isActive) {
  2224. setState("inactive");
  2225. return;
  2226. }
  2227. switch (action) {
  2228. case "down":
  2229. {
  2230. setState("drag");
  2231. didMove = false;
  2232. assert(event, 'Expected event to be defined for "down" action');
  2233. startDragging(resizeHandleId, event);
  2234. const {
  2235. onDragging,
  2236. onPointerDown
  2237. } = callbacksRef.current;
  2238. onDragging === null || onDragging === void 0 ? void 0 : onDragging(true);
  2239. onPointerDown === null || onPointerDown === void 0 ? void 0 : onPointerDown();
  2240. break;
  2241. }
  2242. case "move":
  2243. {
  2244. const {
  2245. state
  2246. } = committedValuesRef.current;
  2247. didMove = true;
  2248. if (state !== "drag") {
  2249. setState("hover");
  2250. }
  2251. assert(event, 'Expected event to be defined for "move" action');
  2252. resizeHandler(event);
  2253. break;
  2254. }
  2255. case "up":
  2256. {
  2257. setState("hover");
  2258. stopDragging();
  2259. const {
  2260. onClick,
  2261. onDragging,
  2262. onPointerUp
  2263. } = callbacksRef.current;
  2264. onDragging === null || onDragging === void 0 ? void 0 : onDragging(false);
  2265. onPointerUp === null || onPointerUp === void 0 ? void 0 : onPointerUp();
  2266. if (!didMove) {
  2267. onClick === null || onClick === void 0 ? void 0 : onClick();
  2268. }
  2269. break;
  2270. }
  2271. }
  2272. };
  2273. return registerResizeHandle(resizeHandleId, element, direction, {
  2274. coarse: coarseHitAreaMargins,
  2275. fine: fineHitAreaMargins
  2276. }, setResizeHandlerState);
  2277. }, [coarseHitAreaMargins, direction, disabled, fineHitAreaMargins, registerResizeHandleWithParentGroup, resizeHandleId, resizeHandler, startDragging, stopDragging]);
  2278. useWindowSplitterResizeHandlerBehavior({
  2279. disabled,
  2280. handleId: resizeHandleId,
  2281. resizeHandler,
  2282. panelGroupElement
  2283. });
  2284. const style = {
  2285. touchAction: "none",
  2286. userSelect: "none"
  2287. };
  2288. return React.createElement(Type, {
  2289. ...rest,
  2290. children,
  2291. className: classNameFromProps,
  2292. id: idFromProps,
  2293. onBlur: () => {
  2294. setIsFocused(false);
  2295. onBlur === null || onBlur === void 0 ? void 0 : onBlur();
  2296. },
  2297. onFocus: () => {
  2298. setIsFocused(true);
  2299. onFocus === null || onFocus === void 0 ? void 0 : onFocus();
  2300. },
  2301. ref: elementRef,
  2302. role: "separator",
  2303. style: {
  2304. ...style,
  2305. ...styleFromProps
  2306. },
  2307. tabIndex,
  2308. // CSS selectors
  2309. [DATA_ATTRIBUTES.groupDirection]: direction,
  2310. [DATA_ATTRIBUTES.groupId]: groupId,
  2311. [DATA_ATTRIBUTES.resizeHandle]: "",
  2312. [DATA_ATTRIBUTES.resizeHandleActive]: state === "drag" ? "pointer" : isFocused ? "keyboard" : undefined,
  2313. [DATA_ATTRIBUTES.resizeHandleEnabled]: !disabled,
  2314. [DATA_ATTRIBUTES.resizeHandleId]: resizeHandleId,
  2315. [DATA_ATTRIBUTES.resizeHandleState]: state
  2316. });
  2317. }
  2318. PanelResizeHandle.displayName = "PanelResizeHandle";
  2319. function getPanelElement(id, scope = document) {
  2320. const element = scope.querySelector(`[data-panel-id="${id}"]`);
  2321. if (element) {
  2322. return element;
  2323. }
  2324. return null;
  2325. }
  2326. function getPanelElementsForGroup(groupId, scope = document) {
  2327. return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
  2328. }
  2329. function getIntersectingRectangle(rectOne, rectTwo, strict) {
  2330. if (!intersects(rectOne, rectTwo, strict)) {
  2331. return {
  2332. x: 0,
  2333. y: 0,
  2334. width: 0,
  2335. height: 0
  2336. };
  2337. }
  2338. return {
  2339. x: Math.max(rectOne.x, rectTwo.x),
  2340. y: Math.max(rectOne.y, rectTwo.y),
  2341. width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
  2342. height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
  2343. };
  2344. }
  2345. exports.DATA_ATTRIBUTES = DATA_ATTRIBUTES;
  2346. exports.Panel = Panel;
  2347. exports.PanelGroup = PanelGroup;
  2348. exports.PanelResizeHandle = PanelResizeHandle;
  2349. exports.assert = assert;
  2350. exports.disableGlobalCursorStyles = disableGlobalCursorStyles;
  2351. exports.enableGlobalCursorStyles = enableGlobalCursorStyles;
  2352. exports.getIntersectingRectangle = getIntersectingRectangle;
  2353. exports.getPanelElement = getPanelElement;
  2354. exports.getPanelElementsForGroup = getPanelElementsForGroup;
  2355. exports.getPanelGroupElement = getPanelGroupElement;
  2356. exports.getResizeHandleElement = getResizeHandleElement;
  2357. exports.getResizeHandleElementIndex = getResizeHandleElementIndex;
  2358. exports.getResizeHandleElementsForGroup = getResizeHandleElementsForGroup;
  2359. exports.getResizeHandlePanelIds = getResizeHandlePanelIds;
  2360. exports.intersects = intersects;
  2361. exports.setNonce = setNonce;