App.jsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. import { uniqueId } from '@tldraw/core'
  2. import React from 'react'
  3. import ReactDOM from 'react-dom'
  4. import { App as TldrawApp } from 'tldraw-logseq'
  5. const storingKey = 'playground.index'
  6. const onPersist = app => {
  7. console.log('onPersist', app)
  8. window.sessionStorage.setItem(storingKey, JSON.stringify(app.serialized))
  9. }
  10. const onLoad = () => {
  11. return JSON.parse(window.sessionStorage.getItem(storingKey))
  12. }
  13. const documentModel = onLoad() ?? {
  14. currentPageId: 'page1',
  15. selectedIds: ['p6bv7EfoQPIF1eZB1RRO6'],
  16. pages: [
  17. {
  18. id: 'page1',
  19. name: 'Page',
  20. shapes: [
  21. {
  22. scale: [1, 1],
  23. blockType: 'B',
  24. id: 'p6bv7EfoQPIF1eZB1RRO6',
  25. type: 'logseq-portal',
  26. parentId: 'page1',
  27. point: [369.109375, 170.5546875],
  28. size: [0, 0],
  29. stroke: '#000000',
  30. fill: '#ffffff',
  31. strokeWidth: 2,
  32. opacity: 1,
  33. pageId: 'aaasssdddfff',
  34. nonce: 1,
  35. },
  36. ],
  37. bindings: {},
  38. nonce: 2,
  39. },
  40. ],
  41. }
  42. const list = ['foo', 'bar']
  43. const Page = props => {
  44. const [value, setValue] = React.useState(JSON.stringify(props, null, 2))
  45. return (
  46. <div className="w-full font-mono page">
  47. The Circle components are a collection of standardized UI elements and patterns for building
  48. products. These pages provide more information and best practices on how to use the
  49. components.The Circle components are a collection of standardized UI elements and patterns for
  50. building products. These pages provide more information and best practices on how to use the
  51. components.
  52. </div>
  53. )
  54. }
  55. const Block = props => {
  56. return (
  57. <div className="w-full font-mono single-block">
  58. The Circle components are a collection of standardized UI elements and patterns for building
  59. products. These pages provide more information and best practices on how to use the
  60. components.The Circle components are a collection of standardized UI elements and patterns for
  61. building products. These pages provide more information and best practices on how to use the
  62. components.
  63. </div>
  64. )
  65. }
  66. const Breadcrumb = props => {
  67. const [value, setValue] = React.useState(JSON.stringify(props))
  68. return (
  69. <input
  70. className="whitespace-pre w-full h-full font-mono"
  71. value={value}
  72. onChange={e => setValue(e.target.value)}
  73. />
  74. )
  75. }
  76. const PageNameLink = props => {
  77. const [value, setValue] = React.useState(JSON.stringify(props))
  78. return (
  79. <input
  80. className="whitespace-pre w-full h-full font-mono"
  81. value={value}
  82. onChange={e => setValue(e.target.value)}
  83. />
  84. )
  85. }
  86. const ThemeSwitcher = ({ theme, setTheme }) => {
  87. const [anchor, setAnchor] = React.useState(null)
  88. React.useEffect(() => {
  89. if (anchor) {
  90. return
  91. }
  92. let el = document.querySelector('#theme-switcher')
  93. if (!el) {
  94. el = document.createElement('div')
  95. el.id = 'theme-switcher'
  96. let timer = setInterval(() => {
  97. const statusBarAnchor = document.querySelector('#tl-statusbar-anchor')
  98. if (statusBarAnchor) {
  99. statusBarAnchor.appendChild(el)
  100. setAnchor(el)
  101. clearInterval(timer)
  102. }
  103. }, 50)
  104. }
  105. })
  106. React.useEffect(() => {
  107. document.documentElement.setAttribute('data-theme', theme)
  108. }, [theme])
  109. if (!anchor) {
  110. return null
  111. }
  112. return ReactDOM.createPortal(
  113. <button
  114. className="flex items-center justify-center mx-2 bg-grey"
  115. style={{ fontSize: '1em' }}
  116. onClick={() => setTheme(t => (t === 'dark' ? 'light' : 'dark'))}
  117. >
  118. {theme} theme
  119. </button>,
  120. anchor
  121. )
  122. }
  123. export default function App() {
  124. const [theme, setTheme] = React.useState('light')
  125. return (
  126. <div className={`h-screen w-screen`}>
  127. <ThemeSwitcher theme={theme} setTheme={setTheme} />
  128. <TldrawApp
  129. renderers={{
  130. Page,
  131. Block,
  132. Breadcrumb,
  133. PageNameLink,
  134. }}
  135. handlers={{
  136. search: q => (q ? list.filter(item => item.includes(q)) : []),
  137. addNewBlock: () => uniqueId(),
  138. }}
  139. model={documentModel}
  140. onPersist={onPersist}
  141. />
  142. </div>
  143. )
  144. }