|
114 | 114 |
|
115 | 115 | (defonce !eval-counter (r/atom 0))
|
116 | 116 |
|
117 |
| -(defn render-processed-block [x] |
118 |
| - (let [{viewer-name :name} (viewer/->viewer x) |
119 |
| - viewer-css-class (viewer/css-class x) |
120 |
| - inner-viewer-name (some-> x viewer/->value viewer/->viewer :name) |
121 |
| - processed-block-id (get-in x [:nextjournal/opts :id])] |
122 |
| - ^{:key (str processed-block-id "@" @!eval-counter)} |
123 |
| - [:div {:data-block-id processed-block-id |
124 |
| - :class (concat |
125 |
| - [(when (:nextjournal/open-graph-image-capture (viewer/->value x)) "open-graph-image-capture")] |
126 |
| - (if viewer-css-class |
127 |
| - (cond-> viewer-css-class |
128 |
| - (string? viewer-css-class) vector) |
129 |
| - ["viewer" |
130 |
| - (when viewer-name (name viewer-name)) |
131 |
| - (when inner-viewer-name (name inner-viewer-name)) |
132 |
| - (case (or (viewer/width x) (case viewer-name (`viewer/code-viewer `viewer/code-folded-viewer) :wide :prose)) |
133 |
| - :wide "w-full max-w-wide" |
134 |
| - :full "w-full" |
135 |
| - "w-full max-w-prose px-8")]))} |
136 |
| - [inspect-presented x]])) |
137 |
| - |
138 | 117 | (defn exec-status [{:keys [progress status]}]
|
139 | 118 | [:div.w-full.bg-purple-200.dark:bg-purple-900.rounded.z-20 {:class "h-0.5"}
|
140 | 119 | [:div.bg-purple-600.dark:bg-purple-400 {:class "h-0.5" :style {:width (str (* progress 100) "%")}}]
|
|
147 | 126 | {:style {:font-size "0.5rem"} :class "left-[35px] md:left-0 mt-[7px] md:mt-1"}
|
148 | 127 | status])
|
149 | 128 |
|
150 |
| -(defn render-notebook [{:as _doc xs :blocks :keys [bundle? css-class sidenotes? toc toc-visibility]}] |
| 129 | +(declare inspect-children) |
| 130 | + |
| 131 | +(defn render-notebook [{:as _doc xs :blocks :keys [bundle? css-class sidenotes? toc toc-visibility]} opts] |
151 | 132 | (r/with-let [local-storage-key "clerk-navbar"
|
152 | 133 | navbar-width 220
|
153 | 134 | !state (r/atom {:toc (toc-items (:children toc))
|
|
197 | 178 | {:class "z-10 fixed right-2 top-2 md:right-auto md:left-3 md:top-[7px] text-slate-400 font-sans text-xs hover:underline cursor-pointer flex items-center bg-white dark:bg-gray-900 py-1 px-3 md:p-0 rounded-full md:rounded-none border md:border-0 border-slate-200 dark:border-gray-500 shadow md:shadow-none dark:text-slate-400 dark:hover:text-white"}]
|
198 | 179 | [navbar/panel !state [navbar/navbar !state]]])
|
199 | 180 | [:div.flex-auto.w-screen.scroll-container
|
200 |
| - [:> (.-div motion) |
201 |
| - {:key "notebook-viewer" |
202 |
| - :initial (when toc-visibility {:margin-left doc-inset}) |
203 |
| - :animate (when toc-visibility {:margin-left doc-inset}) |
204 |
| - :transition navbar/spring |
205 |
| - :class (str (or css-class "flex flex-col items-center notebook-viewer flex-auto ") |
206 |
| - (when sidenotes? "sidenotes-layout"))} |
207 |
| - (doall (map render-processed-block xs))]]]))) |
| 181 | + (into |
| 182 | + [:> (.-div motion) |
| 183 | + {:key "notebook-viewer" |
| 184 | + :initial (when toc-visibility {:margin-left doc-inset}) |
| 185 | + :animate (when toc-visibility {:margin-left doc-inset}) |
| 186 | + :transition navbar/spring |
| 187 | + :class (str (or css-class "flex flex-col items-center notebook-viewer flex-auto ") |
| 188 | + (when sidenotes? "sidenotes-layout"))}] |
| 189 | + |
| 190 | + ;; TODO: restore react keys via block-id |
| 191 | + ;; ^{:key (str processed-block-id "@" @!eval-counter)} |
| 192 | + |
| 193 | + (inspect-children opts) xs)]]))) |
208 | 194 |
|
209 | 195 | (defn opts->query [opts]
|
210 | 196 | (->> opts
|
|
298 | 284 | auto-expand? (-> viewer/assign-content-lengths)
|
299 | 285 | true (-> viewer/assign-expanded-at (get :nextjournal/expanded-at {}))))
|
300 | 286 |
|
301 |
| -(defn render-result [{:as result :nextjournal/keys [fetch-opts hash presented]} {:as opts :keys [auto-expand-results?]}] |
| 287 | +(defn result-css-class [x] |
| 288 | + (let [{viewer-name :name} (viewer/->viewer x) |
| 289 | + viewer-css-class (viewer/css-class x) |
| 290 | + inner-viewer-name (some-> x viewer/->value viewer/->viewer :name)] |
| 291 | + (if viewer-css-class |
| 292 | + (cond-> viewer-css-class |
| 293 | + (string? viewer-css-class) vector) |
| 294 | + ["viewer" |
| 295 | + (when (get-in x [:nextjournal/opts :fragment-item?]) "fragment-item") |
| 296 | + (when viewer-name (name viewer-name)) |
| 297 | + (when inner-viewer-name (name inner-viewer-name)) |
| 298 | + (case (or (viewer/width x) |
| 299 | + (case viewer-name |
| 300 | + (`viewer/code-viewer) :wide |
| 301 | + (`viewer/markdown-node-viewer) :nested-prose |
| 302 | + :prose)) |
| 303 | + :wide "w-full max-w-wide" |
| 304 | + :full "w-full" |
| 305 | + :nested-prose "w-full max-w-prose" |
| 306 | + "w-full max-w-prose px-8")]))) |
| 307 | + |
| 308 | +(defn render-result [{:as result :nextjournal/keys [fetch-opts hash presented]} {:as opts :keys [id auto-expand-results? path]}] |
302 | 309 | (let [!desc (hooks/use-state-with-deps presented [hash])
|
303 | 310 | !expanded-at (hooks/use-state (when (map? @!desc)
|
304 | 311 | (->expanded-at auto-expand-results? @!desc)))
|
|
324 | 331 | (when @!desc
|
325 | 332 | [view-context/provide {:fetch-fn fetch-fn}
|
326 | 333 | [:> ErrorBoundary {:hash hash}
|
327 |
| - [:div.relative |
328 |
| - [:div.overflow-x-auto |
329 |
| - {:ref ref-fn} |
330 |
| - [inspect-presented {:!expanded-at !expanded-at} @!desc]]]]]))) |
| 334 | + [:div.result-viewer {:class (result-css-class @!desc) :data-block-id id :ref ref-fn} |
| 335 | + [:div.relative |
| 336 | + [:div.overflow-x-auto |
| 337 | + {:ref ref-fn} |
| 338 | + [inspect-presented {:!expanded-at !expanded-at} @!desc]]]]]]))) |
331 | 339 |
|
332 | 340 | (defn toggle-expanded [!expanded-at path event]
|
333 | 341 | (.preventDefault event)
|
|
350 | 358 | (defn expandable? [xs]
|
351 | 359 | (< 1 (count xs)))
|
352 | 360 |
|
353 |
| - |
354 | 361 | (defn inspect-children [opts]
|
355 | 362 | ;; TODO: move update function onto viewer
|
356 | 363 | (map-indexed (fn [idx x]
|
357 |
| - (inspect-presented (update opts :path (fnil conj []) idx) x)))) |
| 364 | + (cond-> [inspect-presented (update opts :path (fnil conj []) idx) x] |
| 365 | + (get-in x [:nextjournal/opts :id]) (with-meta {:key (str (get-in x [:nextjournal/opts :id]) "@" @!eval-counter)}))))) |
358 | 366 |
|
359 | 367 | (def expand-style
|
360 | 368 | ["cursor-pointer"
|
|
711 | 719 | (when react-root
|
712 | 720 | (.render react-root (r/as-element [root]))))
|
713 | 721 |
|
| 722 | +(defn render-with-react-key [x {:as _opts :keys [id]}] |
| 723 | + (with-meta x {:key id})) |
| 724 | + |
714 | 725 | (defn html-render [markup]
|
715 | 726 | (r/as-element
|
716 | 727 | (if (string? markup)
|
|
810 | 821 | [:svg {:xmlns "http://www.w3.org/2000/svg" :viewBox "0 0 20 20" :fill "currentColor" :width 12 :height 12}
|
811 | 822 | [:path {:fill-rule "evenodd" :d "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" :clip-rule "evenodd"}]])
|
812 | 823 |
|
813 |
| -(defn render-folded-code [code-string] |
| 824 | +(defn render-code-block [code-string {:keys [id]}] |
| 825 | + ^{:key id} |
| 826 | + [:div.viewer.code-viewer.w-full.max-w-wide {:data-block-id id} |
| 827 | + [code/render-code code-string]]) |
| 828 | + |
| 829 | +(defn render-folded-code-block [code-string {:keys [id]}] |
814 | 830 | (let [!hidden? (hooks/use-state true)]
|
815 | 831 | (if @!hidden?
|
816 | 832 | [:div.relative.pl-12.font-sans.text-slate-400.cursor-pointer.flex.overflow-y-hidden.group
|
|
842 | 858 | [:span.ml-4.opacity-0.translate-y-full.group-hover:opacity-100.group-hover:translate-y-0.transition-all.delay-150.hover:text-slate-500
|
843 | 859 | {:class "text-[10px]"}
|
844 | 860 | "evaluated in 0.2s"]]
|
845 |
| - [:div.code-viewer.mb-2.relative {:style {:margin-top 0}} |
| 861 | + ^{:key id} |
| 862 | + [:div.code-viewer.mb-2.relative.code-viewer.w-full.max-w-wide {:data-block-id id :style {:margin-top 0}} |
846 | 863 | [render-code code-string]]])))
|
847 | 864 |
|
848 | 865 |
|
|
0 commit comments