git.iko.soy/rowtype-yoga/psfp
on master help sign in
Remove autofocus
fb93bc9Mark Eibes
components/src/Yoga/CompileEditor/Component.purs+1−3
@@ -5,10 +5,8 @@ import Data.Array (intercalate)
5import Data.Either (Either(..))
5import Data.Either (Either(..))
6import Data.Foldable (for_)
6import Data.Foldable (for_)
7import Data.Maybe (Maybe(..))
7import Data.Maybe (Maybe(..))
8−import Data.Monoid (guard)
9import Data.Tuple.Nested ((/\))
8import Data.Tuple.Nested ((/\))
10import Data.TwoOrMore (twoOrMore)
9import Data.TwoOrMore (twoOrMore)
11−import Debug.Trace (spy)
12import Effect (Effect)
10import Effect (Effect)
13import Effect.Aff (Error, launchAff_, message, try)
11import Effect.Aff (Error, launchAff_, message, try)
14import Effect.Class (liftEffect)
12import Effect.Class (liftEffect)
@@ -44,7 +42,7 @@ mkCompileEditor { compileAndRun } = do
44 themeMode /\ setThemeMode <- React.useState' Nothing
42 themeMode /\ setThemeMode <- React.useState' Nothing
45 React.useEffectAlways do
43 React.useEffectAlways do
46 mode <- getDarkOrLightMode
44 mode <- getDarkOrLightMode
47− unless (mode == themeMode) do setThemeMode (spy "mode" mode)
45+ unless (mode == themeMode) do setThemeMode mode
48 mempty
46 mempty
49 let
47 let
50 onLoad e = do
48 onLoad e = do
components/src/Yoga/FillInTheGaps/Component.purs+28−36
@@ -4,32 +4,28 @@ import Prelude
4import Color as Color
4import Color as Color
5import Data.Array as A
5import Data.Array as A
6import Data.Foldable (traverse_)
6import Data.Foldable (traverse_)
7−import Data.Maybe (Maybe(..), fromMaybe, isJust)
7+import Data.Maybe (Maybe(..), fromMaybe)
8import Effect (Effect)
8import Effect (Effect)
9−import Literals.Undefined (undefined)
10import Prim.Row (class Lacks)
9import Prim.Row (class Lacks)
11import React.Basic (JSX, ReactComponent, element, elementKeyed)
10import React.Basic (JSX, ReactComponent, element, elementKeyed)
12import React.Basic.DOM as R
11import React.Basic.DOM as R
13import React.Basic.DOM.Events (preventDefault, targetValue)
12import React.Basic.DOM.Events (preventDefault, targetValue)
14import React.Basic.Events (handler, handler_)
13import React.Basic.Events (handler, handler_)
15import React.Basic.Hooks (reactComponent)
14import React.Basic.Hooks (reactComponent)
16−import React.Basic.Hooks as React
17import React.Basic.SyntaxHighlighter.Component (mkHighlighterTheme, syntaxHighlighter)
15import React.Basic.SyntaxHighlighter.Component (mkHighlighterTheme, syntaxHighlighter)
18−import Unsafe.Coerce (unsafeCoerce)
19import Yoga (el)
16import Yoga (el)
20import Yoga.Block as Block
17import Yoga.Block as Block
21import Yoga.Block.Atom.Button.Types as ButtonType
18import Yoga.Block.Atom.Button.Types as ButtonType
22import Yoga.Block.Atom.CodeInput as CodeInput
19import Yoga.Block.Atom.CodeInput as CodeInput
23−import Yoga.DOM.Hook (useFocus)
24import Yoga.FillInTheGaps.Logic (Segment(..), findFirstHoleIndex, holeToFiller, updateSegments)
20import Yoga.FillInTheGaps.Logic (Segment(..), findFirstHoleIndex, holeToFiller, updateSegments)
25import Yoga.Helpers ((?||))
21import Yoga.Helpers ((?||))
26
22
27visibleRange ∷ Array (Array Segment) -> { end ∷ Int, start ∷ Int }
23visibleRange ∷ Array (Array Segment) -> { end ∷ Int, start ∷ Int }
28visibleRange arr = { start, end }
24visibleRange arr = { start, end }
29 where
25 where
30− start = A.findIndex (_ == [ Start ]) arr ?|| 0
26+ start = A.findIndex (_ == [ Start ]) arr ?|| 0
31
27
32− end = A.findIndex (_ == [ End ]) arr ?|| A.length arr
28+ end = A.findIndex (_ == [ End ]) arr ?|| A.length arr
33
29
34mkSegment ∷
30mkSegment ∷
35 ∀ p.
31 ∀ p.
@@ -48,13 +44,11 @@ mkSegment ∷
48 )
44 )
49mkSegment =
45mkSegment =
50 reactComponent "Segment" \{ update, maxLength, text, theKey, focusOnFirstRender } -> React.do
46 reactComponent "Segment" \{ update, maxLength, text, theKey, focusOnFirstRender } -> React.do
51− focusRef <- useFocus
52 pure
47 pure
53 $ elementKeyed CodeInput.component
48 $ elementKeyed CodeInput.component
54 { onChange: handler targetValue (traverse_ update)
49 { onChange: handler targetValue (traverse_ update)
55 , maxLength
50 , maxLength
56 , value: text
51 , value: text
57− , ref: if focusOnFirstRender then focusRef else (unsafeCoerce undefined)
58 , key: theKey
52 , key: theKey
59 }
53 }
60
54
@@ -69,34 +63,34 @@ renderSegments ∷
69 ((Array (Array Segment) -> Array (Array Segment)) -> Effect Unit) -> Array (Array Segment) -> JSX
63 ((Array (Array Segment) -> Array (Array Segment)) -> Effect Unit) -> Array (Array Segment) -> JSX
70renderSegments segment update arrs = R.div_ (A.mapWithIndex renderLine arrs)
64renderSegments segment update arrs = R.div_ (A.mapWithIndex renderLine arrs)
71 where
65 where
72− firstHoleIndex = findFirstHoleIndex arrs
66+ firstHoleIndex = findFirstHoleIndex arrs
73
67
74− { start, end } = visibleRange arrs
68+ { start, end } = visibleRange arrs
75
69
76− renderLine i l = R.div_ (A.mapWithIndex (renderSegment i) l)
70+ renderLine i l = R.div_ (A.mapWithIndex (renderSegment i) l)
77
71
78− renderSegment i j s = case s, between start end i of
72+ renderSegment i j s = case s, between start end i of
79− Filler s', true ->
73+ Filler s', true ->
80− element
74+ element
81− syntaxHighlighter
75+ syntaxHighlighter
82− { style:
76+ { style:
83− mkHighlighterTheme
77+ mkHighlighterTheme
84− { grey: Color.rgb 50 50 50
78+ { grey: Color.rgb 50 50 50
85− , highlightColour: Color.hsl 30.0 0.5 0.4
79+ , highlightColour: Color.hsl 30.0 0.5 0.4
86− , textColour: Color.rgb 0 0 1
80+ , textColour: Color.rgb 0 0 1
87− }
81+ }
88− , language: "purescript"
82+ , language: "purescript"
89− , children: s'
83+ , children: s'
90− }
84+ }
91− Hole maxLength text, true ->
85+ Hole maxLength text, true ->
92− element segment
86+ element segment
93− { maxLength
87+ { maxLength
94− , theKey: show i <> "," <> show j
88+ , theKey: show i <> "," <> show j
95− , update: update <<< updateSegments i j
89+ , update: update <<< updateSegments i j
96− , text
90+ , text
97− , focusOnFirstRender: (firstHoleIndex <#> \fh -> fh.i == i && fh.j == j) # fromMaybe false
91+ , focusOnFirstRender: (firstHoleIndex <#> \fh -> fh.i == i && fh.j == j) # fromMaybe false
98− }
92+ }
99− _, _ -> mempty
93+ _, _ -> mempty
100
94
101type Props =
95type Props =
102 { segments ∷ Array (Array Segment)
96 { segments ∷ Array (Array Segment)
@@ -109,7 +103,6 @@ makeComponent ∷ Effect (ReactComponent Props)
109makeComponent = do
103makeComponent = do
110 segment <- mkSegment
104 segment <- mkSegment
111 reactComponent "FillInTheGaps" \(props@{ updateSegments, segments, run, solvedWith } ∷ Props) -> React.do
105 reactComponent "FillInTheGaps" \(props@{ updateSegments, segments, run, solvedWith } ∷ Props) -> React.do
112− ref <- useFocus
113 pure
106 pure
114 $ R.form
107 $ R.form
115 { onSubmit: handler preventDefault (const run)
108 { onSubmit: handler preventDefault (const run)
@@ -128,7 +121,6 @@ makeComponent = do
128 [ el Block.button
121 [ el Block.button
129 { onClick: handler_ mempty
122 { onClick: handler_ mempty
130 , buttonType: ButtonType.Primary
123 , buttonType: ButtonType.Primary
131− , ref: if isJust (findFirstHoleIndex segments) then unsafeCoerce undefined else ref
132 }
124 }
133 [ R.text "Run" ]
125 [ R.text "Run" ]
134 ]
126 ]
components/src/Yoga/Monaco/Editor.js+85−44
@@ -82,6 +82,7 @@ exports.colorizeImpl = function (text) {
82 };
82 };
83};
83};
84exports.setValue = function (value) { return function (editor) { return function () { return editor.setValue(value); }; }; };
84exports.setValue = function (value) { return function (editor) { return function () { return editor.setValue(value); }; }; };
85+exports.layout = (editor) => () => editor.layout()
85exports.defineThemeImpl = function (monacoInstance) {
86exports.defineThemeImpl = function (monacoInstance) {
86 return function (name) {
87 return function (name) {
87 return function (theme) {
88 return function (theme) {
@@ -114,99 +115,138 @@ exports.setMonarchTokensProviderImpl = function (monacoInstance) {
114 };
115 };
115 };
116 };
116};
117};
117−exports.vsCodeTheme = function (bg) {
118+exports.horizonTheme = function (bg) {
118 return ({
119 return ({
119 base: "vs",
120 base: "vs",
120 inherit: true,
121 inherit: true,
121 rules: [
122 rules: [
122 {
123 {
123− foreground: "008e00",
124+ foreground: "#33333380",
125+ fontStyle: "italic",
124− token: "comment",
126+ token: "comment"
125 },
127 },
126 {
128 {
127− foreground: "7d4726",
129+ foreground: "#DC3318",
128− token: "meta.preprocessor",
130+ token: "constant"
129 },
131 },
130 {
132 {
131− foreground: "7d4726",
133+ foreground: "#F77D26",
132− token: "keyword.control.import",
134+ token: "entity.name"
133 },
135 },
134 {
136 {
135− foreground: "df0002",
137+ foreground: "#1D8991",
136− token: "string",
138+ token: "entity.name.function"
137 },
139 },
138 {
140 {
139− foreground: "3a00dc",
141+ foreground: "#DA103F",
140− token: "constant.numeric",
142+ token: "entity.name.tag"
141 },
143 },
142 {
144 {
143− foreground: "c800a4",
145+ foreground: "#F77D26",
144− token: "constant.language",
146+ token: "entity.name.type"
145 },
147 },
146 {
148 {
147− foreground: "275a5e",
149+ foreground: "#DA103F",
148− token: "constant.character",
150+ token: "variable"
149 },
151 },
150 {
152 {
151− foreground: "275a5e",
153+ foreground: "#DA103F",
152− token: "constant.other",
154+ token: "entity.name.variable"
153 },
155 },
154 {
156 {
155− foreground: "c800a4",
157+ foreground: "#8A31B9",
156− token: "variable.language",
158+ token: "keyword"
157 },
159 },
158 {
160 {
159− foreground: "c800a4",
161+ foreground: "#333333",
160− token: "variable.other",
162+ token: "keyword.operator"
161 },
163 },
162 {
164 {
163− foreground: "c800a4",
165+ foreground: "#8A31B9",
164− token: "keyword",
166+ token: "keyword.operator.new"
165 },
167 },
166 {
168 {
167− foreground: "c900a4",
169+ foreground: "#8A31B9",
168− token: "storage",
170+ token: "keyword.operator.expression"
169 },
171 },
170 {
172 {
171− foreground: "438288",
173+ foreground: "#8A31B9",
172− token: "entity.name.class",
174+ token: "keyword.operator.logical, keyword.operator.delete"
173 },
175 },
174 {
176 {
175− foreground: "790ead",
177+ foreground: "#8A31B9",
176− token: "entity.name.tag",
178+ token: "keyword.operator.delete"
177 },
179 },
178 {
180 {
179− foreground: "450084",
181+ foreground: "#DC3318",
180− token: "entity.other.attribute-name",
182+ token: "keyword.other.unit"
181 },
183 },
182 {
184 {
183− foreground: "450084",
185+ foreground: "#F6661EB3",
184− token: "support.function",
186+ fontStyle: "italic",
187+ token: "markup.quote"
185 },
188 },
186 {
189 {
187− foreground: "450084",
190+ foreground: "#DA103F",
188− token: "support.constant",
191+ token: "entity.name.section"
189 },
192 },
190 {
193 {
191− foreground: "790ead",
194+ foreground: "#8A31B9",
192− token: "support.type",
195+ token: "storage"
193 },
196 },
194 {
197 {
195− foreground: "790ead",
198+ foreground: "#F6661E",
196− token: "support.class",
199+ token: "string"
197 },
200 },
198 {
201 {
199− foreground: "790ead",
202+ foreground: "#F77D26",
203+ token: "support"
204+ },
205+ {
206+ foreground: "#1D8991",
200− token: "support.other.variable",
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"
201 },
241 },
202 ],
242 ],
203 colors: {
243 colors: {
204 "editor.foreground": "#000000",
244 "editor.foreground": "#000000",
205− "editor.background": bg,
245+ "editor.background": bg, //"#FDF0ED"
206− "editor.selectionBackground": "#B5D5FF",
246+ "editor.selectionBackground": "#F9CBBE80",
207 "editor.lineHighlightBackground": "#B5D5FF",
247 "editor.lineHighlightBackground": "#B5D5FF",
208 "editor.lineHighlightBorder": "#B5D5FF",
248 "editor.lineHighlightBorder": "#B5D5FF",
209− "editorCursor.foreground": "#000000",
249+ "editorCursor.foreground": "#333333",
210 "editorWhitespace.foreground": "#BFBFBF",
250 "editorWhitespace.foreground": "#BFBFBF",
211 },
251 },
212 });
252 });
@@ -218,6 +258,7 @@ exports.nightOwlTheme = function (bg) {
218 rules: [
258 rules: [
219 {
259 {
220 foreground: "637777",
260 foreground: "637777",
261+ fontStyle: "italic",
221 token: "comment",
262 token: "comment",
222 },
263 },
223 {
264 {
components/src/Yoga/Monaco/Editor.purs+41−27
@@ -1,13 +1,15 @@
1module Yoga.Editor where
1module Yoga.Editor where
2
2
3import Prelude
3import 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)
5import CSS.Overflow (hidden, overflowY)
5import CSS.Overflow (hidden, overflowY)
6import Control.Promise (Promise)
6import Control.Promise (Promise)
7import Control.Promise as Promise
7import Control.Promise as Promise
8−import Data.Foldable (for_)
8+import Data.Foldable (fold, foldMap, for_)
9import Data.Maybe (Maybe(..), maybe)
9import Data.Maybe (Maybe(..), maybe)
10+import Data.Monoid (guard)
10−import Data.Nullable (Nullable)
11+import Data.Nullable (Nullable, notNull, null)
12+import Data.Time.Duration (Seconds(..))
11import Data.Tuple.Nested ((/\))
13import Data.Tuple.Nested ((/\))
12import Effect (Effect)
14import Effect (Effect)
13import Effect.Aff (Aff)
15import Effect.Aff (Aff)
@@ -19,16 +21,14 @@ import JSS (jssClasses)
19import Prim.Row (class Union)
21import Prim.Row (class Union)
20import React.Basic (JSX, ReactComponent, Ref, element, fragment)
22import React.Basic (JSX, ReactComponent, Ref, element, fragment)
21import React.Basic.DOM as R
23import 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)
24import React.Basic.Hooks as React
25import React.Basic.Hooks as React
25import React.Basic.Hooks.Aff (useAff)
26import React.Basic.Hooks.Aff (useAff)
26import Web.DOM (Node)
27import Web.DOM (Node)
27import Web.HTML (HTMLElement)
28import Web.HTML (HTMLElement)
28−import Yoga.Theme (fromTheme)
29+import Yoga.Block.Container.Style (DarkOrLightMode(..), getDarkOrLightMode)
29−import Yoga.Theme.Default (darkTheme)
30+import Yoga.Block.Hook.UseResize (useOnResize)
30import Yoga.Theme.Styles (makeStylesJSS)
31import Yoga.Theme.Styles (makeStylesJSS)
31−import Yoga.Theme.Types (CSSTheme)
32
32
33type EditorProps =
33type EditorProps =
34 ( value ∷ String
34 ( value ∷ String
@@ -52,7 +52,7 @@ foreign import setThemeImpl ∷ Monaco -> String -> Effect Unit
52
52
53foreign import nightOwlTheme ∷ String -> MonacoTheme
53foreign import nightOwlTheme ∷ String -> MonacoTheme
54
54
55−foreign import vsCodeTheme ∷ String -> MonacoTheme
55+foreign import horizonTheme ∷ String -> MonacoTheme
56
56
57foreign import getValue ∷ Editor -> Effect String
57foreign import getValue ∷ Editor -> Effect String
58
58
@@ -60,6 +60,8 @@ foreign import setValue ∷ String -> Editor -> Effect Unit
60
60
61foreign import colorizeImpl ∷ String -> String -> { tabSize ∷ Int } -> Editor -> Promise String
61foreign import colorizeImpl ∷ String -> String -> { tabSize ∷ Int } -> Editor -> Promise String
62
62
63+foreign import layout ∷ Editor -> Effect Unit
64+
63foreign import data Monaco ∷ Type
65foreign import data Monaco ∷ Type
64
66
65foreign import data Editor ∷ Type
67foreign import data Editor ∷ Type
@@ -75,7 +77,7 @@ darkThemeName ∷ String
75darkThemeName = "NightOwl"
77darkThemeName = "NightOwl"
76
78
77lightThemeName ∷ String
79lightThemeName ∷ String
78−lightThemeName = "VSCode"
80+lightThemeName = "Horizon"
79
81
80foreign import data MonarchLanguage ∷ Type
82foreign import data MonarchLanguage ∷ Type
81
83
@@ -85,10 +87,10 @@ foreign import registerLanguageImpl ∷ Monaco -> String -> Effect Unit
85
87
86foreign import setMonarchTokensProviderImpl ∷ Monaco -> String -> MonarchLanguage -> Effect Unit
88foreign import setMonarchTokensProviderImpl ∷ Monaco -> String -> MonarchLanguage -> Effect Unit
87
89
88−initEditor ∷ CSSTheme -> Monaco -> Effect Unit
90+initEditor ∷ Monaco -> Effect Unit
89−initEditor theme monaco = do
91+initEditor monaco = do
90− defineThemeImpl monaco darkThemeName (nightOwlTheme (toHexString theme.backgroundColour))
92+ defineThemeImpl monaco darkThemeName (nightOwlTheme "#212134") -- [TODO] Read from somewhere else
91− defineThemeImpl monaco lightThemeName (vsCodeTheme (toHexString theme.backgroundColour))
93+ defineThemeImpl monaco lightThemeName (horizonTheme "#EBDFDE") -- [TODO] Don't hardcode
92 registerLanguageImpl monaco "purescript"
94 registerLanguageImpl monaco "purescript"
93 setMonarchTokensProviderImpl monaco "purescript" purescriptSyntax
95 setMonarchTokensProviderImpl monaco "purescript" purescriptSyntax
94
96
@@ -111,26 +113,35 @@ mkEditor = do
111 padding (4.0 # px) (8.0 # px) (0.0 # px) (8.0 # px)
113 padding (4.0 # px) (8.0 # px) (0.0 # px) (8.0 # px)
112 width (100.0 # pct)
114 width (100.0 # pct)
113 overflowY hidden
115 overflowY hidden
114− -- backgroundColor (colour.background) [TODO]
116+ -- backgroundColor (colour.background) [ TODO ]
115 }
117 }
116 reactComponent "Editor" \{ onLoad, height, language } -> React.do
118 reactComponent "Editor" \{ onLoad, height, language } -> React.do
117 classes <- useStyles {}
119 classes <- useStyles {}
118− maybeEditor /\ modifyEditor <- useState Nothing
120+ mbEditorComponent /\ setEditorComponent <- React.useState' Nothing
119− maybeMonaco /\ modifyMonaco <- 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
120 useAff unit do
126 useAff unit do
121− eddy <- monacoEditor
127+ med <- monacoEditor
122− liftEffect $ modifyEditor (const (Just eddy))
128+ setEditorComponent (Just med) # liftEffect
123− useEffect unit do -- [TODO] notice theme change
129+ useEffectAlways do
124− for_ maybeMonaco (initEditor (fromTheme darkTheme))
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
125− pure mempty
136+ mempty
126 let themeName = darkThemeName
137 let themeName = darkThemeName
127 pure
138 pure
128 $ fragment
139 $ fragment
129 [ R.div
140 [ R.div
130 { className: classes.wrapper
141 { className: classes.wrapper
131 , children:
142 , children:
132− [ maybeEditor
143+ [ mbEditorComponent
133− # maybe mempty \editor ->
144+ # foldMap \editor ->
134 element editor
145 element editor
135 { theme: themeName
146 { theme: themeName
136 , height
147 , height
@@ -154,11 +165,14 @@ mkEditor = do
154 }
165 }
155 , language
166 , language
156 -- https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-custom-languages
167 -- 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
158 , editorWillMount:
172 , editorWillMount:
159 mkEffectFn1 \m -> do
173 mkEffectFn1 \m -> do
160− modifyMonaco (const $ Just m)
174+ React.writeRef monacoRef (notNull m)
161− (initEditor (fromTheme darkTheme) m)
175+ initEditor m
162 }
176 }
163 ]
177 ]
164 }
178 }