Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

debug-panel.tsx 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /* eslint-disable @typescript-eslint/ban-ts-comment */
  2. import styled from 'styles'
  3. import React, { useRef } from 'react'
  4. import state, { useSelector } from 'state'
  5. import * as Panel from 'components/panel'
  6. import {
  7. breakpoints,
  8. IconButton,
  9. RowButton,
  10. IconWrapper,
  11. } from 'components/shared'
  12. import {
  13. Cross2Icon,
  14. PlayIcon,
  15. DotIcon,
  16. CrumpledPaperIcon,
  17. StopIcon,
  18. ClipboardIcon,
  19. ClipboardCopyIcon,
  20. TrashIcon,
  21. } from '@radix-ui/react-icons'
  22. import logger from 'state/logger'
  23. import { useStateDesigner } from '@state-designer/react'
  24. const stopPropagation = (e: React.KeyboardEvent) => e.stopPropagation()
  25. const toggleDebugPanel = () => state.send('TOGGLED_DEBUG_PANEL')
  26. const handleStateCopy = () => state.send('COPIED_STATE_TO_CLIPBOARD')
  27. const handleError = () => {
  28. throw Error('Error!')
  29. }
  30. export default function CodePanel(): JSX.Element {
  31. const rContainer = useRef<HTMLDivElement>(null)
  32. const isDebugging = useSelector((s) => s.data.settings.isDebugMode)
  33. const isOpen = useSelector((s) => s.data.settings.isDebugOpen)
  34. const rTextArea = useRef<HTMLTextAreaElement>(null)
  35. const local = useStateDesigner({
  36. initial: 'stopped',
  37. data: {
  38. log: '',
  39. },
  40. states: {
  41. stopped: {
  42. on: {
  43. CHANGED_LOG: 'setLog',
  44. COPIED_LOG: { if: 'hasLog', do: 'copyLog' },
  45. PLAYED_BACK_LOG: { if: 'hasLog', do: 'playbackLog' },
  46. STARTED_LOGGING: { do: 'startLogger', to: 'logging' },
  47. },
  48. },
  49. logging: {
  50. on: {
  51. STOPPED_LOGGING: { do: 'stopLogger', to: 'stopped' },
  52. },
  53. },
  54. },
  55. conditions: {
  56. hasLog(data) {
  57. return data.log !== ''
  58. },
  59. },
  60. actions: {
  61. setLog(data, payload: { value: string }) {
  62. data.log = payload.value
  63. },
  64. startLogger(data) {
  65. logger.start(state.data)
  66. data.log = ''
  67. },
  68. stopLogger(data) {
  69. logger.stop(state.data)
  70. data.log = logger.copyToJson()
  71. },
  72. playbackLog(data) {
  73. logger.playback(state.data, data.log)
  74. },
  75. copyLog() {
  76. logger.copyToJson()
  77. },
  78. },
  79. })
  80. if (!isDebugging) return null
  81. const handleLoggingStop = () => local.send('STOPPED_LOGGING')
  82. const handlePlayback = () =>
  83. local.send('PLAYED_BACK_LOG', { log: rTextArea.current?.value })
  84. const handleLoggingStart = () => local.send('STARTED_LOGGING')
  85. const handleLoggingCopy = () => local.send('COPIED_DEBUG_LOG')
  86. return (
  87. <StylePanelRoot
  88. dir="ltr"
  89. bp={breakpoints}
  90. data-bp-desktop
  91. ref={rContainer}
  92. variant="code"
  93. onWheel={(e) => e.stopPropagation()}
  94. >
  95. {isOpen ? (
  96. <Panel.Layout onKeyDown={stopPropagation}>
  97. <Panel.Header side="left">
  98. <IconButton
  99. bp={breakpoints}
  100. size="small"
  101. onClick={toggleDebugPanel}
  102. >
  103. <Cross2Icon />
  104. </IconButton>
  105. <span>Debugging</span>
  106. <div />
  107. </Panel.Header>
  108. <Panel.Content>
  109. <hr />
  110. <RowButton bp={breakpoints} onClick={handleStateCopy}>
  111. <span>Copy State</span>
  112. <IconWrapper size="small">
  113. <ClipboardCopyIcon />
  114. </IconWrapper>
  115. </RowButton>
  116. <RowButton bp={breakpoints} onClick={handleError}>
  117. <span>Create Error</span>
  118. <IconWrapper size="small">
  119. <TrashIcon />
  120. </IconWrapper>
  121. </RowButton>
  122. <hr />
  123. {local.isIn('stopped') ? (
  124. <RowButton bp={breakpoints} onClick={handleLoggingStart}>
  125. <span>Start Logger</span>
  126. <IconWrapper size="small">
  127. <DotIcon />
  128. </IconWrapper>
  129. </RowButton>
  130. ) : (
  131. <RowButton bp={breakpoints} onClick={handleLoggingStop}>
  132. <span>Stop Logger</span>
  133. <IconWrapper size="small">
  134. <StopIcon />
  135. </IconWrapper>
  136. </RowButton>
  137. )}
  138. <JSONTextAreaWrapper>
  139. <IconButton
  140. bp={breakpoints}
  141. onClick={handleLoggingCopy}
  142. disabled={!local.can('COPIED_LOG')}
  143. style={{ position: 'absolute', top: 2, right: 2 }}
  144. >
  145. <ClipboardIcon />
  146. </IconButton>
  147. <JSONTextArea
  148. ref={rTextArea}
  149. value={local.data.log}
  150. onChange={(e) =>
  151. local.send('CHANGED_LOG', { value: e.currentTarget.value })
  152. }
  153. />
  154. </JSONTextAreaWrapper>
  155. <RowButton
  156. bp={breakpoints}
  157. onClick={handlePlayback}
  158. disabled={!local.can('PLAYED_BACK_LOG')}
  159. >
  160. <span>Play Back Log</span>
  161. <IconWrapper size="small">
  162. <PlayIcon />
  163. </IconWrapper>
  164. </RowButton>
  165. </Panel.Content>
  166. </Panel.Layout>
  167. ) : (
  168. <IconButton bp={breakpoints} size="small" onClick={toggleDebugPanel}>
  169. <CrumpledPaperIcon />
  170. </IconButton>
  171. )}
  172. </StylePanelRoot>
  173. )
  174. }
  175. const StylePanelRoot = styled(Panel.Root, {
  176. width: 'fit-content',
  177. maxWidth: 'fit-content',
  178. overflow: 'hidden',
  179. position: 'relative',
  180. border: '1px solid $panel',
  181. boxShadow: '$4',
  182. display: 'flex',
  183. flexDirection: 'column',
  184. alignItems: 'center',
  185. pointerEvents: 'all',
  186. padding: '$0',
  187. '& hr': {
  188. marginTop: 2,
  189. marginBottom: 2,
  190. marginLeft: '-$0',
  191. border: 'none',
  192. height: 1,
  193. backgroundColor: '$brushFill',
  194. width: 'calc(100% + 4px)',
  195. },
  196. })
  197. const JSONTextAreaWrapper = styled('div', {
  198. position: 'relative',
  199. margin: '4px 0',
  200. })
  201. const JSONTextArea = styled('textarea', {
  202. minHeight: '100px',
  203. width: '100%',
  204. font: '$mono',
  205. backgroundColor: '$panel',
  206. border: '1px solid $border',
  207. borderRadius: '4px',
  208. padding: '4px',
  209. outline: 'none',
  210. })