layout-context.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import {
  2. createContext,
  3. useContext,
  4. useCallback,
  5. useMemo,
  6. useEffect,
  7. } from 'react'
  8. import PropTypes from 'prop-types'
  9. import useScopeValue from '../hooks/use-scope-value'
  10. import usePreviousValue from '../hooks/use-previous-value'
  11. import useDetachLayout from '../hooks/use-detach-layout'
  12. import { useIdeContext } from './ide-context'
  13. import localStorage from '../../infrastructure/local-storage'
  14. export const LayoutContext = createContext()
  15. LayoutContext.Provider.propTypes = {
  16. value: PropTypes.shape({
  17. view: PropTypes.string,
  18. setView: PropTypes.func.isRequired,
  19. chatIsOpen: PropTypes.bool,
  20. setChatIsOpen: PropTypes.func.isRequired,
  21. reviewPanelOpen: PropTypes.bool,
  22. setReviewPanelOpen: PropTypes.func.isRequired,
  23. leftMenuShown: PropTypes.bool,
  24. setLeftMenuShown: PropTypes.func.isRequired,
  25. pdfLayout: PropTypes.oneOf(['sideBySide', 'flat']).isRequired,
  26. }).isRequired,
  27. }
  28. export function LayoutProvider({ children }) {
  29. const { $scope } = useIdeContext()
  30. // what to show in the "flat" view (editor or pdf)
  31. const [view, _setView] = useScopeValue('ui.view')
  32. const setView = useCallback(
  33. value => {
  34. _setView(oldValue => {
  35. // ensure that the "history:toggle" event is broadcast when switching in or out of history view
  36. if (value === 'history' || oldValue === 'history') {
  37. $scope.toggleHistory()
  38. }
  39. return value
  40. })
  41. },
  42. [$scope, _setView]
  43. )
  44. // whether the chat pane is open
  45. const [chatIsOpen, setChatIsOpen] = useScopeValue('ui.chatOpen')
  46. // whether the review pane is open
  47. const [reviewPanelOpen, setReviewPanelOpen] = useScopeValue(
  48. 'ui.reviewPanelOpen'
  49. )
  50. // whether the menu pane is open
  51. const [leftMenuShown, setLeftMenuShown] = useScopeValue('ui.leftMenuShown')
  52. // whether to display the editor and preview side-by-side or full-width ("flat")
  53. const [pdfLayout, setPdfLayout] = useScopeValue('ui.pdfLayout')
  54. // switch to either side-by-side or flat (full-width) layout
  55. const switchLayout = useCallback(() => {
  56. setPdfLayout(layout => {
  57. const newLayout = layout === 'sideBySide' ? 'flat' : 'sideBySide'
  58. setView(newLayout === 'sideBySide' ? 'editor' : 'pdf')
  59. setPdfLayout(newLayout)
  60. localStorage.setItem('pdf.layout', newLayout)
  61. })
  62. }, [setPdfLayout, setView])
  63. const changeLayout = useCallback(
  64. (newLayout, newView) => {
  65. setPdfLayout(newLayout)
  66. setView(newLayout === 'sideBySide' ? 'editor' : newView)
  67. localStorage.setItem('pdf.layout', newLayout)
  68. },
  69. [setPdfLayout, setView]
  70. )
  71. const {
  72. reattach,
  73. detach,
  74. mode: detachMode,
  75. role: detachRole,
  76. } = useDetachLayout()
  77. const previousDetachMode = usePreviousValue(detachMode)
  78. useEffect(() => {
  79. switch (detachMode) {
  80. case 'detacher':
  81. changeLayout('flat', 'editor')
  82. break
  83. case 'detaching':
  84. changeLayout('flat', 'editor')
  85. break
  86. case 'detached':
  87. break
  88. case 'orphan':
  89. break
  90. case null:
  91. if (previousDetachMode) {
  92. changeLayout('sideBySide')
  93. }
  94. break
  95. }
  96. }, [detachMode, previousDetachMode, changeLayout])
  97. const value = useMemo(
  98. () => ({
  99. reattach,
  100. detach,
  101. detachMode,
  102. detachRole,
  103. changeLayout,
  104. chatIsOpen,
  105. leftMenuShown,
  106. pdfLayout,
  107. reviewPanelOpen,
  108. setChatIsOpen,
  109. setLeftMenuShown,
  110. setPdfLayout,
  111. setReviewPanelOpen,
  112. setView,
  113. switchLayout,
  114. view,
  115. }),
  116. [
  117. reattach,
  118. detach,
  119. detachMode,
  120. detachRole,
  121. changeLayout,
  122. chatIsOpen,
  123. leftMenuShown,
  124. pdfLayout,
  125. reviewPanelOpen,
  126. setChatIsOpen,
  127. setLeftMenuShown,
  128. setPdfLayout,
  129. setReviewPanelOpen,
  130. setView,
  131. switchLayout,
  132. view,
  133. ]
  134. )
  135. return (
  136. <LayoutContext.Provider value={value}>{children}</LayoutContext.Provider>
  137. )
  138. }
  139. LayoutProvider.propTypes = {
  140. children: PropTypes.any,
  141. }
  142. export function useLayoutContext(propTypes) {
  143. const data = useContext(LayoutContext)
  144. PropTypes.checkPropTypes(propTypes, data, 'data', 'LayoutContext.Provider')
  145. return data
  146. }