[#15578] fix code snippet
This commit is contained in:
parent
a261628b83
commit
ccb20d7994
|
@ -0,0 +1,87 @@
|
||||||
|
(ns quo2.components.code.code.style
|
||||||
|
(:require [quo2.foundations.colors :as colors]))
|
||||||
|
|
||||||
|
;; Example themes:
|
||||||
|
;; https://github.com/react-syntax-highlighter/react-syntax-highlighter/tree/master/src/styles/hljs
|
||||||
|
(defn theme
|
||||||
|
[theme-key]
|
||||||
|
(case theme-key
|
||||||
|
:hljs-comment (colors/theme-colors colors/neutral-40 colors/neutral-60)
|
||||||
|
:hljs-title (colors/custom-color-by-theme :sky 50 60)
|
||||||
|
:hljs-keyword (colors/custom-color-by-theme :green 50 60)
|
||||||
|
:hljs-string (colors/custom-color-by-theme :turquoise 50 60)
|
||||||
|
:hljs-literal (colors/custom-color-by-theme :turquoise 50 60)
|
||||||
|
:hljs-number (colors/custom-color-by-theme :turquoise 50 60)
|
||||||
|
:line-number colors/neutral-40
|
||||||
|
nil))
|
||||||
|
|
||||||
|
(defn text-style
|
||||||
|
[class-names]
|
||||||
|
(let [text-color (->> class-names
|
||||||
|
(map keyword)
|
||||||
|
(some (fn [class-name]
|
||||||
|
(when-let [text-color (theme class-name)]
|
||||||
|
text-color))))]
|
||||||
|
(cond-> {:flex-shrink 1
|
||||||
|
:line-height 18}
|
||||||
|
text-color (assoc :color text-color))))
|
||||||
|
|
||||||
|
(defn border-color
|
||||||
|
[]
|
||||||
|
(colors/theme-colors colors/neutral-20 colors/neutral-80))
|
||||||
|
|
||||||
|
(defn container
|
||||||
|
[]
|
||||||
|
{:overflow :hidden
|
||||||
|
:padding 8
|
||||||
|
:background-color (colors/theme-colors colors/white colors/neutral-80-opa-40)
|
||||||
|
:border-color (border-color)
|
||||||
|
:border-width 1
|
||||||
|
:border-radius 16})
|
||||||
|
|
||||||
|
(def gradient-container
|
||||||
|
{:position :absolute
|
||||||
|
:bottom 0
|
||||||
|
:left 0
|
||||||
|
:right 0
|
||||||
|
:z-index 1})
|
||||||
|
|
||||||
|
(def gradient {:height 48})
|
||||||
|
|
||||||
|
(defn line-number-container
|
||||||
|
[line-number-width]
|
||||||
|
{:position :absolute
|
||||||
|
:bottom 0
|
||||||
|
:top 0
|
||||||
|
:left 0
|
||||||
|
:width (+ line-number-width 8 7)
|
||||||
|
:background-color (colors/theme-colors colors/neutral-5 colors/neutral-80)})
|
||||||
|
|
||||||
|
(defn divider
|
||||||
|
[line-number-width]
|
||||||
|
{:position :absolute
|
||||||
|
:bottom 0
|
||||||
|
:top 0
|
||||||
|
:left (+ line-number-width 7 7)
|
||||||
|
:width 1
|
||||||
|
:z-index 2
|
||||||
|
:background-color (border-color)})
|
||||||
|
|
||||||
|
(def line {:flex-direction :row})
|
||||||
|
|
||||||
|
(defn line-number
|
||||||
|
[width]
|
||||||
|
{:margin-right 20 ; 8+12 margin
|
||||||
|
:width width})
|
||||||
|
|
||||||
|
(def copy-button
|
||||||
|
{:position :absolute
|
||||||
|
:bottom 8
|
||||||
|
:right 8
|
||||||
|
:z-index 1})
|
||||||
|
|
||||||
|
(defn gradient-color [] (colors/theme-colors colors/white colors/neutral-80))
|
||||||
|
|
||||||
|
(defn button-background-color
|
||||||
|
[]
|
||||||
|
(colors/theme-colors colors/neutral-80-opa-5 colors/white-opa-5))
|
|
@ -1,175 +1,110 @@
|
||||||
(ns quo2.components.code.snippet
|
(ns quo2.components.code.snippet
|
||||||
(:require [cljs-bean.core :as bean]
|
(:require [cljs-bean.core :as bean]
|
||||||
[clojure.string :as string]
|
[clojure.string :as string]
|
||||||
[oops.core :as oops]
|
|
||||||
[quo2.components.buttons.button :as button]
|
[quo2.components.buttons.button :as button]
|
||||||
|
[quo2.components.code.code.style :as style]
|
||||||
[quo2.components.markdown.text :as text]
|
[quo2.components.markdown.text :as text]
|
||||||
[quo2.foundations.colors :as colors]
|
|
||||||
[quo2.theme :as theme]
|
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
[react-native.linear-gradient :as linear-gradient]
|
[react-native.linear-gradient :as linear-gradient]
|
||||||
[react-native.masked-view :as masked-view]
|
|
||||||
[react-native.syntax-highlighter :as highlighter]
|
[react-native.syntax-highlighter :as highlighter]
|
||||||
[reagent.core :as reagent]))
|
[reagent.core :as reagent]))
|
||||||
|
|
||||||
;; Example themes:
|
|
||||||
;; https://github.com/react-syntax-highlighter/react-syntax-highlighter/tree/master/src/styles/hljs
|
|
||||||
(def ^:private themes
|
|
||||||
{:light {:hljs-comment {:color colors/neutral-40}
|
|
||||||
:hljs-title {:color (colors/custom-color :blue 50)}
|
|
||||||
:hljs-keyword {:color (colors/custom-color :green 50)}
|
|
||||||
:hljs-string {:color (colors/custom-color :turquoise 50)}
|
|
||||||
:hljs-literal {:color (colors/custom-color :turquoise 50)}
|
|
||||||
:hljs-number {:color (colors/custom-color :turquoise 50)}
|
|
||||||
:line-number {:color colors/neutral-40}}
|
|
||||||
:dark {:hljs-comment {:color colors/neutral-60}
|
|
||||||
:hljs-title {:color (colors/custom-color :blue 60)}
|
|
||||||
:hljs-keyword {:color (colors/custom-color :green 60)}
|
|
||||||
:hljs-string {:color (colors/custom-color :turquoise 60)}
|
|
||||||
:hljs-literal {:color (colors/custom-color :turquoise 60)}
|
|
||||||
:hljs-number {:color (colors/custom-color :turquoise 60)}
|
|
||||||
:line-number {:color colors/neutral-40}}})
|
|
||||||
|
|
||||||
(defn- text-style
|
|
||||||
[class-names]
|
|
||||||
(->> class-names
|
|
||||||
(map keyword)
|
|
||||||
(reduce #(merge %1 (get-in themes [(theme/get-theme) %2]))
|
|
||||||
{:flex-shrink 1
|
|
||||||
;; Round to a nearest whole number to achieve consistent
|
|
||||||
;; spacing (also important for calculating `max-text-height`).
|
|
||||||
;; Line height seems to be inconsistent between text being
|
|
||||||
;; wrapped and text being rendered on a newline using Flexbox
|
|
||||||
;; layout.
|
|
||||||
:line-height 18})))
|
|
||||||
|
|
||||||
(defn- render-nodes
|
(defn- render-nodes
|
||||||
[nodes]
|
[nodes]
|
||||||
(map (fn [{:keys [children value] :as node}]
|
(map (fn [{:keys [children value last-line?] :as node}]
|
||||||
;; Node can have :children or a :value.
|
|
||||||
(if children
|
(if children
|
||||||
(into [text/text
|
(into [text/text
|
||||||
{:weight :code
|
(cond-> {:weight :code
|
||||||
:size :paragraph-2
|
:size :paragraph-2
|
||||||
:style (text-style (get-in node [:properties :className]))}]
|
:style (style/text-style (get-in node [:properties :className]))}
|
||||||
|
last-line? (assoc :number-of-lines 1))]
|
||||||
(render-nodes children))
|
(render-nodes children))
|
||||||
;; Remove newlines as we already render each line separately.
|
;; Remove newlines as we already render each line separately.
|
||||||
(-> value string/trim-newline)))
|
(string/trim-newline value)))
|
||||||
nodes))
|
nodes))
|
||||||
|
|
||||||
|
(defn- line
|
||||||
|
[{:keys [line-number line-number-width]} children]
|
||||||
|
[rn/view {:style style/line}
|
||||||
|
[rn/view {:style (style/line-number line-number-width)}
|
||||||
|
[text/text
|
||||||
|
{:style (style/text-style ["line-number"])
|
||||||
|
:weight :code
|
||||||
|
:size :paragraph-2}
|
||||||
|
line-number]]
|
||||||
|
children])
|
||||||
|
|
||||||
(defn- code-block
|
(defn- code-block
|
||||||
[{:keys [rows line-number-width]}]
|
[{:keys [rows line-number-width]}]
|
||||||
[into [:<>]
|
[rn/view
|
||||||
(->> rows
|
(->> rows
|
||||||
(render-nodes)
|
(render-nodes)
|
||||||
;; Line numbers
|
(map-indexed (fn [idx row-content]
|
||||||
(map-indexed (fn [idx row]
|
[line
|
||||||
(conj [rn/view {:style {:flex-direction :row}}
|
{:line-number (inc idx)
|
||||||
[rn/view
|
:line-number-width line-number-width}
|
||||||
{:style {:width line-number-width
|
row-content]))
|
||||||
;; 8+12 margin
|
(into [:<>]))])
|
||||||
:margin-right 20}}
|
|
||||||
[text/text
|
(defn- mask-view
|
||||||
{:weight :code
|
[{:keys [apply-mask?]} child]
|
||||||
:size :paragraph-2
|
(if apply-mask?
|
||||||
:style (text-style ["line-number"])}
|
[:<>
|
||||||
(inc idx)]]]
|
[rn/view {:style style/gradient-container}
|
||||||
row))))])
|
[linear-gradient/linear-gradient
|
||||||
|
{:style style/gradient
|
||||||
|
:colors [:transparent (style/gradient-color)]}
|
||||||
|
[rn/view {:style style/gradient}]]]
|
||||||
|
child]
|
||||||
|
child))
|
||||||
|
|
||||||
|
(defn- calc-line-number-width
|
||||||
|
[font-scale rows-to-show]
|
||||||
|
(let [max-line-digits (-> rows-to-show str count)]
|
||||||
|
(if (= 1 max-line-digits)
|
||||||
|
18 ;; ~ 9 is char width, 18 is width used in Figma.
|
||||||
|
(* 9 max-line-digits font-scale))))
|
||||||
|
|
||||||
(defn- native-renderer
|
(defn- native-renderer
|
||||||
[]
|
[{:keys [rows max-lines on-copy-press]
|
||||||
(let [text-height (reagent/atom nil)]
|
:or {max-lines ##Inf}}]
|
||||||
(fn [{:keys [rows max-lines on-copy-press]}]
|
(let [font-scale (:font-scale (rn/use-window-dimensions))
|
||||||
(let [background-color (colors/theme-colors
|
total-rows (count rows)
|
||||||
colors/white
|
number-rows-to-show (min (count rows) max-lines)
|
||||||
colors/neutral-80-opa-40)
|
line-number-width (calc-line-number-width font-scale number-rows-to-show)
|
||||||
background-color-left (colors/theme-colors
|
truncated? (< number-rows-to-show total-rows)
|
||||||
colors/neutral-5
|
rows-to-show-coll (if truncated?
|
||||||
colors/neutral-80)
|
(as-> rows $
|
||||||
border-color (colors/theme-colors
|
(update $ number-rows-to-show assoc :last-line? true)
|
||||||
colors/neutral-20
|
(take (inc number-rows-to-show) $))
|
||||||
colors/neutral-80)
|
rows)]
|
||||||
rows (bean/->clj rows)
|
[rn/view {:style (style/container)}
|
||||||
font-scale (:font-scale (rn/use-window-dimensions))
|
[rn/view {:style (style/line-number-container line-number-width)}]
|
||||||
max-rows (or max-lines (count rows)) ;; Cut down on rows to process.
|
[rn/view {:style (style/divider line-number-width)}]
|
||||||
max-line-digits (-> rows count (min max-rows) str count)
|
[mask-view {:apply-mask? truncated?}
|
||||||
;; ~ 9 is char width, 18 is width used in Figma.
|
[code-block
|
||||||
line-number-width (* font-scale (max 18 (* 9 max-line-digits)))
|
{:rows rows-to-show-coll
|
||||||
max-text-height (some-> max-lines
|
:line-number-width line-number-width}]]
|
||||||
(* font-scale 18)) ;; 18 is font's line height.
|
[rn/view {:style style/copy-button}
|
||||||
truncated? (and max-text-height (< max-text-height @text-height))
|
[button/button
|
||||||
maybe-mask-wrapper (if truncated?
|
{:icon true
|
||||||
[masked-view/masked-view
|
:type :grey
|
||||||
{:mask-element
|
:size 24
|
||||||
(reagent/as-element
|
:on-press on-copy-press
|
||||||
[linear-gradient/linear-gradient
|
:override-background-color (style/button-background-color)}
|
||||||
{:colors ["black" "transparent"]
|
:main-icons/copy]]]))
|
||||||
:locations [0.75 1]
|
|
||||||
:style {:flex 1}}])}]
|
|
||||||
[:<>])]
|
|
||||||
|
|
||||||
[rn/view
|
|
||||||
{:style {:overflow :hidden
|
|
||||||
:padding 8
|
|
||||||
:background-color background-color
|
|
||||||
:border-color border-color
|
|
||||||
:border-width 1
|
|
||||||
:border-radius 8
|
|
||||||
;; Hide on intial render to avoid flicker when mask-wrapper is shown.
|
|
||||||
:opacity (if @text-height 1 0)}}
|
|
||||||
;; Line number container
|
|
||||||
[rn/view
|
|
||||||
{:style {:position :absolute
|
|
||||||
:bottom 0
|
|
||||||
:top 0
|
|
||||||
:left 0
|
|
||||||
:width (+ line-number-width 8 8)
|
|
||||||
:background-color background-color-left
|
|
||||||
:border-right-color border-color
|
|
||||||
:border-right-width 1}}]
|
|
||||||
(conj maybe-mask-wrapper
|
|
||||||
[rn/view {:max-height max-text-height}
|
|
||||||
[rn/view
|
|
||||||
{:on-layout (fn [evt]
|
|
||||||
(let [height (oops/oget evt "nativeEvent.layout.height")]
|
|
||||||
(reset! text-height height)))}
|
|
||||||
[code-block
|
|
||||||
{:rows (take max-rows rows)
|
|
||||||
:line-number-width line-number-width}]]])
|
|
||||||
|
|
||||||
;; Copy button
|
|
||||||
[rn/view
|
|
||||||
{:style {:position :absolute
|
|
||||||
:bottom 8
|
|
||||||
:right 8}}
|
|
||||||
[button/button
|
|
||||||
{:icon true
|
|
||||||
:type :grey
|
|
||||||
:size 24
|
|
||||||
:on-press on-copy-press}
|
|
||||||
:main-icons/copy]]]))))
|
|
||||||
|
|
||||||
(defn- wrap-renderer-fn
|
|
||||||
[f {:keys [max-lines on-copy-press]}]
|
|
||||||
(fn [^js props]
|
|
||||||
(reagent/as-element [:f> f
|
|
||||||
{:rows (.-rows props)
|
|
||||||
:max-lines max-lines
|
|
||||||
:on-copy-press on-copy-press}])))
|
|
||||||
|
|
||||||
(defn snippet
|
(defn snippet
|
||||||
[{:keys [language max-lines on-copy-press]} children]
|
[{:keys [language max-lines on-copy-press]} children]
|
||||||
[highlighter/highlighter
|
[highlighter/highlighter
|
||||||
{:language language
|
{:language language
|
||||||
:renderer (wrap-renderer-fn
|
:renderer (fn [^js/Object props]
|
||||||
native-renderer
|
(reagent/as-element
|
||||||
{:max-lines max-lines
|
[:f> native-renderer
|
||||||
:on-copy-press #(when on-copy-press
|
{:rows (-> props .-rows bean/->clj)
|
||||||
(on-copy-press children))})
|
:on-copy-press #(when on-copy-press (on-copy-press children))
|
||||||
;; Default props to adapt Highlighter for react-native.
|
:max-lines max-lines}]))
|
||||||
;;:CodeTag react-native/View
|
|
||||||
;;:PreTag react-native/View
|
|
||||||
:show-line-numbers false
|
:show-line-numbers false
|
||||||
:style #js {}
|
:style {}
|
||||||
:custom-style #js {:backgroundColor nil}}
|
:custom-style {:background-color nil}}
|
||||||
children])
|
children])
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
(ns react-native.syntax-highlighter
|
(ns react-native.syntax-highlighter
|
||||||
(:require ["react-syntax-highlighter" :default Highlighter]
|
(:require ["react-native" :as react-native]
|
||||||
[reagent.core :as reagent]))
|
["react-syntax-highlighter" :default Highlighter]))
|
||||||
|
|
||||||
(def highlighter (reagent/adapt-react-class Highlighter))
|
|
||||||
|
(defn highlighter
|
||||||
|
[props code-string]
|
||||||
|
[:> Highlighter
|
||||||
|
;; Default props to adapt Highlighter for react-native.
|
||||||
|
(assoc props :Code-tag react-native/View :Pre-tag react-native/View)
|
||||||
|
code-string])
|
||||||
|
|
|
@ -26,6 +26,23 @@
|
||||||
s.logger.Error(\"failed to set Server.port\", zap.Error(err))
|
s.logger.Error(\"failed to set Server.port\", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.afterPortChanged != nil {
|
||||||
|
s.afterPortChanged(s.port)
|
||||||
|
}
|
||||||
|
s.run = true
|
||||||
|
|
||||||
|
err = s.server.Serve(listener)
|
||||||
|
if err != http.ErrServerClosed {
|
||||||
|
s.logger.Error(\"server failed unexpectedly, restarting\", zap.Error(err))
|
||||||
|
err = s.Start()
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error(\"server start failed, giving up\", zap.Error(err))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.run = false
|
||||||
}")
|
}")
|
||||||
|
|
||||||
(def clojure-example
|
(def clojure-example
|
||||||
|
@ -59,9 +76,13 @@
|
||||||
:value :clojure}
|
:value :clojure}
|
||||||
{:key :go
|
{:key :go
|
||||||
:value :go}]}
|
:value :go}]}
|
||||||
{:label "Max lines:"
|
{:label "Max lines:"
|
||||||
:key :max-lines
|
:key :max-lines
|
||||||
:type :text}
|
:type :select
|
||||||
|
:options (map (fn [n]
|
||||||
|
{:key n
|
||||||
|
:value (str n " lines")})
|
||||||
|
(range 0 41 5))}
|
||||||
{:label "Syntax highlight:"
|
{:label "Syntax highlight:"
|
||||||
:key :syntax
|
:key :syntax
|
||||||
:type :boolean}])
|
:type :boolean}])
|
||||||
|
@ -69,7 +90,7 @@
|
||||||
(defn cool-preview
|
(defn cool-preview
|
||||||
[]
|
[]
|
||||||
(let [state (reagent/atom {:language :clojure
|
(let [state (reagent/atom {:language :clojure
|
||||||
:max-lines ""
|
:max-lines 40
|
||||||
:syntax true})]
|
:syntax true})]
|
||||||
(fn []
|
(fn []
|
||||||
(let [language (if (:syntax @state) (:language @state) :text)
|
(let [language (if (:syntax @state) (:language @state) :text)
|
||||||
|
|
Loading…
Reference in New Issue