git.iko.soy/rowtype-yoga/psfp
on master help sign in
Remove autofocus
commitfb93bc9c9447a0c01b07c6c88605b3c7c21e7f03 authorMark Eibes <mark.eibes@gmail.com> dateSun 10 Jan 2021, 00:08 (6 years ago) parentf2e3678
4 files changed+155−110
components/src/Yoga/CompileEditor/Component.purs+1−3 components/src/Yoga/FillInTheGaps/Component.purs+28−36 components/src/Yoga/Monaco/Editor.js+85−44 components/src/Yoga/Monaco/Editor.purs+41−27
components/src/Yoga/CompileEditor/Component.purs+1−3
@@ -5,10 +5,8 @@ import Data.Array (intercalate)
55import Data.Either (Either(..))
66import Data.Foldable (for_)
77import Data.Maybe (Maybe(..))
8−import Data.Monoid (guard)
98import Data.Tuple.Nested ((/\))
109import Data.TwoOrMore (twoOrMore)
11−import Debug.Trace (spy)
1210import Effect (Effect)
1311import Effect.Aff (Error, launchAff_, message, try)
1412import Effect.Class (liftEffect)
@@ -44,7 +42,7 @@ mkCompileEditor { compileAndRun } = do
4442 themeMode /\ setThemeMode <- React.useState' Nothing
4543 React.useEffectAlways do
4644 mode <- getDarkOrLightMode
47− unless (mode == themeMode) do setThemeMode (spy "mode" mode)
45+ unless (mode == themeMode) do setThemeMode mode
4846 mempty
4947 let
5048 onLoad e = do
components/src/Yoga/FillInTheGaps/Component.purs+28−36
@@ -4,32 +4,28 @@ import Prelude
44import Color as Color
55import Data.Array as A
66import Data.Foldable (traverse_)
7−import Data.Maybe (Maybe(..), fromMaybe, isJust)
7+import Data.Maybe (Maybe(..), fromMaybe)
88import Effect (Effect)
9−import Literals.Undefined (undefined)
109import Prim.Row (class Lacks)
1110import React.Basic (JSX, ReactComponent, element, elementKeyed)
1211import React.Basic.DOM as R
1312import React.Basic.DOM.Events (preventDefault, targetValue)
1413import React.Basic.Events (handler, handler_)
1514import React.Basic.Hooks (reactComponent)
16−import React.Basic.Hooks as React
1715import React.Basic.SyntaxHighlighter.Component (mkHighlighterTheme, syntaxHighlighter)
18−import Unsafe.Coerce (unsafeCoerce)
1916import Yoga (el)
2017import Yoga.Block as Block
2118import Yoga.Block.Atom.Button.Types as ButtonType
2219import Yoga.Block.Atom.CodeInput as CodeInput
23−import Yoga.DOM.Hook (useFocus)
2420import Yoga.FillInTheGaps.Logic (Segment(..), findFirstHoleIndex, holeToFiller, updateSegments)
2521import Yoga.Helpers ((?||))
2622
2723visibleRange ∷ Array (Array Segment) -> { end ∷ Int, start ∷ Int }
2824visibleRange arr = { start, end }
2925 where
30− start = A.findIndex (_ == [ Start ]) arr ?|| 0
26+ start = A.findIndex (_ == [ Start ]) arr ?|| 0
3127
32− end = A.findIndex (_ == [ End ]) arr ?|| A.length arr
28+ end = A.findIndex (_ == [ End ]) arr ?|| A.length arr
3329
3430mkSegment ∷
3531 ∀ p.
@@ -48,13 +44,11 @@ mkSegment ∷
4844 )
4945mkSegment =
5046 reactComponent "Segment" \{ update, maxLength, text, theKey, focusOnFirstRender } -> React.do
51− focusRef <- useFocus
5247 pure
5348 $ elementKeyed CodeInput.component
5449 { onChange: handler targetValue (traverse_ update)
5550 , maxLength
5651 , value: text
57− , ref: if focusOnFirstRender then focusRef else (unsafeCoerce undefined)
5852 , key: theKey
5953 }
6054
@@ -69,34 +63,34 @@ renderSegments ∷
6963 ((Array (Array Segment) -> Array (Array Segment)) -> Effect Unit) -> Array (Array Segment) -> JSX
7064renderSegments segment update arrs = R.div_ (A.mapWithIndex renderLine arrs)
7165 where
72− firstHoleIndex = findFirstHoleIndex arrs
66+ firstHoleIndex = findFirstHoleIndex arrs
7367
74− { start, end } = visibleRange arrs
68+ { start, end } = visibleRange arrs
7569
76− renderLine i l = R.div_ (A.mapWithIndex (renderSegment i) l)
70+ renderLine i l = R.div_ (A.mapWithIndex (renderSegment i) l)
7771
78− renderSegment i j s = case s, between start end i of
79− Filler s', true ->
80− element
81− syntaxHighlighter
82− { style:
83− mkHighlighterTheme
84− { grey: Color.rgb 50 50 50
85− , highlightColour: Color.hsl 30.0 0.5 0.4
86− , textColour: Color.rgb 0 0 1
87− }
88− , language: "purescript"
89− , children: s'
90− }
91− Hole maxLength text, true ->
92− element segment
93− { maxLength
94− , theKey: show i <> "," <> show j
95− , update: update <<< updateSegments i j
96− , text
97− , focusOnFirstRender: (firstHoleIndex <#> \fh -> fh.i == i && fh.j == j) # fromMaybe false
98− }
99− _, _ -> mempty
72+ renderSegment i j s = case s, between start end i of
73+ Filler s', true ->
74+ element
75+ syntaxHighlighter
76+ { style:
77+ mkHighlighterTheme
78+ { grey: Color.rgb 50 50 50
79+ , highlightColour: Color.hsl 30.0 0.5 0.4
80+ , textColour: Color.rgb 0 0 1
81+ }
82+ , language: "purescript"
83+ , children: s'
84+ }
85+ Hole maxLength text, true ->
86+ element segment
87+ { maxLength
88+ , theKey: show i <> "," <> show j
89+ , update: update <<< updateSegments i j
90+ , text
91+ , focusOnFirstRender: (firstHoleIndex <#> \fh -> fh.i == i && fh.j == j) # fromMaybe false
92+ }
93+ _, _ -> mempty
10094
10195type Props =
10296 { segments ∷ Array (Array Segment)
@@ -109,7 +103,6 @@ makeComponent ∷ Effect (ReactComponent Props)
109103makeComponent = do
110104 segment <- mkSegment
111105 reactComponent "FillInTheGaps" \(props@{ updateSegments, segments, run, solvedWith } ∷ Props) -> React.do
112− ref <- useFocus
113106 pure
114107 $ R.form
115108 { onSubmit: handler preventDefault (const run)
@@ -128,7 +121,6 @@ makeComponent = do
128121 [ el Block.button
129122 { onClick: handler_ mempty
130123 , buttonType: ButtonType.Primary
131− , ref: if isJust (findFirstHoleIndex segments) then unsafeCoerce undefined else ref
132124 }
133125 [ R.text "Run" ]
134126 ]
components/src/Yoga/Monaco/Editor.js+85−44
@@ -82,6 +82,7 @@ exports.colorizeImpl = function (text) {
8282 };
8383};
8484exports.setValue = function (value) { return function (editor) { return function () { return editor.setValue(value); }; }; };
85+exports.layout = (editor) => () => editor.layout()
8586exports.defineThemeImpl = function (monacoInstance) {
8687 return function (name) {
8788 return function (theme) {
@@ -114,99 +115,138 @@ exports.setMonarchTokensProviderImpl = function (monacoInstance) {
114115 };
115116 };
116117};
117−exports.vsCodeTheme = function (bg) {
118+exports.horizonTheme = function (bg) {
118119 return ({
119120 base: "vs",
120121 inherit: true,
121122 rules: [
122123 {
123− foreground: "008e00",
124− token: "comment",
124+ foreground: "#33333380",
125+ fontStyle: "italic",
126+ token: "comment"
125127 },
126128 {
127− foreground: "7d4726",
128− token: "meta.preprocessor",
129+ foreground: "#DC3318",
130+ token: "constant"
129131 },
130132 {
131− foreground: "7d4726",
132− token: "keyword.control.import",
133+ foreground: "#F77D26",
134+ token: "entity.name"
133135 },
134136 {
135− foreground: "df0002",
136− token: "string",
137+ foreground: "#1D8991",
138+ token: "entity.name.function"
137139 },
138140 {
139− foreground: "3a00dc",
140− token: "constant.numeric",
141+ foreground: "#DA103F",
142+ token: "entity.name.tag"
141143 },
142144 {
143− foreground: "c800a4",
144− token: "constant.language",
145+ foreground: "#F77D26",
146+ token: "entity.name.type"
145147 },
146148 {
147− foreground: "275a5e",
148− token: "constant.character",
149+ foreground: "#DA103F",
150+ token: "variable"
149151 },
150152 {
151− foreground: "275a5e",
152− token: "constant.other",
153+ foreground: "#DA103F",
154+ token: "entity.name.variable"
153155 },
154156 {
155− foreground: "c800a4",
156− token: "variable.language",
157+ foreground: "#8A31B9",
158+ token: "keyword"
157159 },
158160 {
159− foreground: "c800a4",
160− token: "variable.other",
161+ foreground: "#333333",
162+ token: "keyword.operator"
161163 },
162164 {
163− foreground: "c800a4",
164− token: "keyword",
165+ foreground: "#8A31B9",
166+ token: "keyword.operator.new"
165167 },
166168 {
167− foreground: "c900a4",
168− token: "storage",
169+ foreground: "#8A31B9",
170+ token: "keyword.operator.expression"
169171 },
170172 {
171− foreground: "438288",
172− token: "entity.name.class",
173+ foreground: "#8A31B9",
174+ token: "keyword.operator.logical, keyword.operator.delete"
173175 },
174176 {
175− foreground: "790ead",
176− token: "entity.name.tag",
177+ foreground: "#8A31B9",
178+ token: "keyword.operator.delete"
177179 },
178180 {
179− foreground: "450084",
180− token: "entity.other.attribute-name",
181+ foreground: "#DC3318",
182+ token: "keyword.other.unit"
181183 },
182184 {
183− foreground: "450084",
184− token: "support.function",
185+ foreground: "#F6661EB3",
186+ fontStyle: "italic",
187+ token: "markup.quote"
185188 },
186189 {
187− foreground: "450084",
188− token: "support.constant",
190+ foreground: "#DA103F",
191+ token: "entity.name.section"
189192 },
190193 {
191− foreground: "790ead",
192− token: "support.type",
194+ foreground: "#8A31B9",
195+ token: "storage"
193196 },
194197 {
195− foreground: "790ead",
196− token: "support.class",
198+ foreground: "#F6661E",
199+ token: "string"
197200 },
198201 {
199− foreground: "790ead",
200− token: "support.other.variable",
202+ foreground: "#F77D26",
203+ token: "support"
204+ },
205+ {
206+ foreground: "#1D8991",
207+ token: "support.function"
208+ },
209+ {
210+ foreground: "#DA103F",
211+ token: "support.variable"
212+ },
213+ {
214+ foreground: "#DA103F",
215+ token: "support.type.property-name"
216+ },
217+ {
218+ foreground: "#DA103F",
219+ token: "meta.object-literal.key"
220+ },
221+ {
222+ foreground: "#333333",
223+ token: "support.type.property-name.css"
224+ },
225+ {
226+ foreground: "#F77D26",
227+ fontStyle: "italic",
228+ token: "variable.language"
229+ },
230+ {
231+ fontStyle: "italic",
232+ token: "variable.parameter"
233+ },
234+ {
235+ foreground: "#DA103FB3",
236+ token: "punctuation.definition.tag"
237+ },
238+ {
239+ foreground: "#333333",
240+ token: "punctuation.separator"
201241 },
202242 ],
203243 colors: {
204244 "editor.foreground": "#000000",
205− "editor.background": bg,
206− "editor.selectionBackground": "#B5D5FF",
245+ "editor.background": bg, //"#FDF0ED"
246+ "editor.selectionBackground": "#F9CBBE80",
207247 "editor.lineHighlightBackground": "#B5D5FF",
208248 "editor.lineHighlightBorder": "#B5D5FF",
209− "editorCursor.foreground": "#000000",
249+ "editorCursor.foreground": "#333333",
210250 "editorWhitespace.foreground": "#BFBFBF",
211251 },
212252 });
@@ -218,6 +258,7 @@ exports.nightOwlTheme = function (bg) {
218258 rules: [
219259 {
220260 foreground: "637777",
261+ fontStyle: "italic",
221262 token: "comment",
222263 },
223264 {
components/src/Yoga/Monaco/Editor.purs+41−27
@@ -1,13 +1,15 @@
11module Yoga.Editor where
22
33import Prelude
4−import CSS (borderBox, borderRadius, boxSizing, calc, margin, padding, pct, px, toHexString, unitless, width)
4+import CSS (borderBox, borderRadius, boxSizing, margin, padding, pct, px, unitless, width)
55import CSS.Overflow (hidden, overflowY)
66import Control.Promise (Promise)
77import Control.Promise as Promise
8−import Data.Foldable (for_)
8+import Data.Foldable (fold, foldMap, for_)
99import Data.Maybe (Maybe(..), maybe)
10−import Data.Nullable (Nullable)
10+import Data.Monoid (guard)
11+import Data.Nullable (Nullable, notNull, null)
12+import Data.Time.Duration (Seconds(..))
1113import Data.Tuple.Nested ((/\))
1214import Effect (Effect)
1315import Effect.Aff (Aff)
@@ -19,16 +21,14 @@ import JSS (jssClasses)
1921import Prim.Row (class Union)
2022import React.Basic (JSX, ReactComponent, Ref, element, fragment)
2123import React.Basic.DOM as R
22−import React.Basic.Emotion (var)
23−import React.Basic.Hooks (reactComponent, useEffect, useState)
24+import React.Basic.Hooks (reactComponent, useEffect, useEffectAlways, useState)
2425import React.Basic.Hooks as React
2526import React.Basic.Hooks.Aff (useAff)
2627import Web.DOM (Node)
2728import Web.HTML (HTMLElement)
28−import Yoga.Theme (fromTheme)
29−import Yoga.Theme.Default (darkTheme)
29+import Yoga.Block.Container.Style (DarkOrLightMode(..), getDarkOrLightMode)
30+import Yoga.Block.Hook.UseResize (useOnResize)
3031import Yoga.Theme.Styles (makeStylesJSS)
31−import Yoga.Theme.Types (CSSTheme)
3232
3333type EditorProps =
3434 ( value ∷ String
@@ -52,7 +52,7 @@ foreign import setThemeImpl ∷ Monaco -> String -> Effect Unit
5252
5353foreign import nightOwlTheme ∷ String -> MonacoTheme
5454
55−foreign import vsCodeTheme ∷ String -> MonacoTheme
55+foreign import horizonTheme ∷ String -> MonacoTheme
5656
5757foreign import getValue ∷ Editor -> Effect String
5858
@@ -60,6 +60,8 @@ foreign import setValue ∷ String -> Editor -> Effect Unit
6060
6161foreign import colorizeImpl ∷ String -> String -> { tabSize ∷ Int } -> Editor -> Promise String
6262
63+foreign import layout ∷ Editor -> Effect Unit
64+
6365foreign import data Monaco ∷ Type
6466
6567foreign import data Editor ∷ Type
@@ -75,7 +77,7 @@ darkThemeName ∷ String
7577darkThemeName = "NightOwl"
7678
7779lightThemeName ∷ String
78−lightThemeName = "VSCode"
80+lightThemeName = "Horizon"
7981
8082foreign import data MonarchLanguage ∷ Type
8183
@@ -85,10 +87,10 @@ foreign import registerLanguageImpl ∷ Monaco -> String -> Effect Unit
8587
8688foreign import setMonarchTokensProviderImpl ∷ Monaco -> String -> MonarchLanguage -> Effect Unit
8789
88−initEditor ∷ CSSTheme -> Monaco -> Effect Unit
89−initEditor theme monaco = do
90− defineThemeImpl monaco darkThemeName (nightOwlTheme (toHexString theme.backgroundColour))
91− defineThemeImpl monaco lightThemeName (vsCodeTheme (toHexString theme.backgroundColour))
90+initEditor ∷ Monaco -> Effect Unit
91+initEditor monaco = do
92+ defineThemeImpl monaco darkThemeName (nightOwlTheme "#212134") -- [TODO] Read from somewhere else
93+ defineThemeImpl monaco lightThemeName (horizonTheme "#EBDFDE") -- [TODO] Don't hardcode
9294 registerLanguageImpl monaco "purescript"
9395 setMonarchTokensProviderImpl monaco "purescript" purescriptSyntax
9496
@@ -111,26 +113,35 @@ mkEditor = do
111113 padding (4.0 # px) (8.0 # px) (0.0 # px) (8.0 # px)
112114 width (100.0 # pct)
113115 overflowY hidden
114− -- backgroundColor (colour.background) [TODO]
116+ -- backgroundColor (colour.background) [ TODO ]
115117 }
116118 reactComponent "Editor" \{ onLoad, height, language } -> React.do
117119 classes <- useStyles {}
118− maybeEditor /\ modifyEditor <- useState Nothing
119− maybeMonaco /\ modifyMonaco <- useState Nothing
120+ mbEditorComponent /\ setEditorComponent <- React.useState' Nothing
121+ monacoRef <- React.useRef null
122+ editorRef <- React.useRef null
123+ useOnResize (3.0 # Seconds) \_ -> do
124+ mbEditor <- React.readRefMaybe editorRef
125+ for_ mbEditor layout
120126 useAff unit do
121− eddy <- monacoEditor
122− liftEffect $ modifyEditor (const (Just eddy))
123− useEffect unit do -- [TODO] notice theme change
124− for_ maybeMonaco (initEditor (fromTheme darkTheme))
125− pure mempty
127+ med <- monacoEditor
128+ setEditorComponent (Just med) # liftEffect
129+ useEffectAlways do
130+ mbMode <- getDarkOrLightMode
131+ mbMonaco <- React.readRefMaybe monacoRef
132+ case mbMode, mbMonaco of
133+ Just DarkMode, Just monaco -> setThemeImpl monaco darkThemeName
134+ Just LightMode, Just monaco -> setThemeImpl monaco lightThemeName
135+ _, _ -> mempty
136+ mempty
126137 let themeName = darkThemeName
127138 pure
128139 $ fragment
129140 [ R.div
130141 { className: classes.wrapper
131142 , children:
132− [ maybeEditor
133− # maybe mempty \editor ->
143+ [ mbEditorComponent
144+ # foldMap \editor ->
134145 element editor
135146 { theme: themeName
136147 , height
@@ -154,11 +165,14 @@ mkEditor = do
154165 }
155166 , language
156167 -- https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-custom-languages
157− , editorDidMount: mkEffectFn2 \e _ -> onLoad e
168+ , editorDidMount:
169+ mkEffectFn2 \e _ -> do
170+ React.writeRef editorRef (notNull e)
171+ onLoad e
158172 , editorWillMount:
159173 mkEffectFn1 \m -> do
160− modifyMonaco (const $ Just m)
161− (initEditor (fromTheme darkTheme) m)
174+ React.writeRef monacoRef (notNull m)
175+ initEditor m
162176 }
163177 ]
164178 }