Remove autofocus

This commit is contained in:
Mark Eibes 2021-01-10 00:08:44 +01:00
parent f2e3678bc9
commit fb93bc9c94
4 changed files with 155 additions and 110 deletions

View File

@ -5,10 +5,8 @@ import Data.Array (intercalate)
import Data.Either (Either(..))
import Data.Foldable (for_)
import Data.Maybe (Maybe(..))
import Data.Monoid (guard)
import Data.Tuple.Nested ((/\))
import Data.TwoOrMore (twoOrMore)
import Debug.Trace (spy)
import Effect (Effect)
import Effect.Aff (Error, launchAff_, message, try)
import Effect.Class (liftEffect)
@ -44,7 +42,7 @@ mkCompileEditor { compileAndRun } = do
themeMode /\ setThemeMode <- React.useState' Nothing
React.useEffectAlways do
mode <- getDarkOrLightMode
unless (mode == themeMode) do setThemeMode (spy "mode" mode)
unless (mode == themeMode) do setThemeMode mode
mempty
let
onLoad e = do

View File

@ -4,32 +4,28 @@ import Prelude
import Color as Color
import Data.Array as A
import Data.Foldable (traverse_)
import Data.Maybe (Maybe(..), fromMaybe, isJust)
import Data.Maybe (Maybe(..), fromMaybe)
import Effect (Effect)
import Literals.Undefined (undefined)
import Prim.Row (class Lacks)
import React.Basic (JSX, ReactComponent, element, elementKeyed)
import React.Basic.DOM as R
import React.Basic.DOM.Events (preventDefault, targetValue)
import React.Basic.Events (handler, handler_)
import React.Basic.Hooks (reactComponent)
import React.Basic.Hooks as React
import React.Basic.SyntaxHighlighter.Component (mkHighlighterTheme, syntaxHighlighter)
import Unsafe.Coerce (unsafeCoerce)
import Yoga (el)
import Yoga.Block as Block
import Yoga.Block.Atom.Button.Types as ButtonType
import Yoga.Block.Atom.CodeInput as CodeInput
import Yoga.DOM.Hook (useFocus)
import Yoga.FillInTheGaps.Logic (Segment(..), findFirstHoleIndex, holeToFiller, updateSegments)
import Yoga.Helpers ((?||))
visibleRange ∷ Array (Array Segment) -> { end ∷ Int, start ∷ Int }
visibleRange arr = { start, end }
where
start = A.findIndex (_ == [ Start ]) arr ?|| 0
start = A.findIndex (_ == [ Start ]) arr ?|| 0
end = A.findIndex (_ == [ End ]) arr ?|| A.length arr
end = A.findIndex (_ == [ End ]) arr ?|| A.length arr
mkSegment ∷
∀ p.
@ -48,13 +44,11 @@ mkSegment ∷
)
mkSegment =
reactComponent "Segment" \{ update, maxLength, text, theKey, focusOnFirstRender } -> React.do
focusRef <- useFocus
pure
$ elementKeyed CodeInput.component
{ onChange: handler targetValue (traverse_ update)
, maxLength
, value: text
, ref: if focusOnFirstRender then focusRef else (unsafeCoerce undefined)
, key: theKey
}
@ -69,34 +63,34 @@ renderSegments ∷
((Array (Array Segment) -> Array (Array Segment)) -> Effect Unit) -> Array (Array Segment) -> JSX
renderSegments segment update arrs = R.div_ (A.mapWithIndex renderLine arrs)
where
firstHoleIndex = findFirstHoleIndex arrs
firstHoleIndex = findFirstHoleIndex arrs
{ start, end } = visibleRange arrs
{ start, end } = visibleRange arrs
renderLine i l = R.div_ (A.mapWithIndex (renderSegment i) l)
renderLine i l = R.div_ (A.mapWithIndex (renderSegment i) l)
renderSegment i j s = case s, between start end i of
Filler s', true ->
element
syntaxHighlighter
{ style:
mkHighlighterTheme
{ grey: Color.rgb 50 50 50
, highlightColour: Color.hsl 30.0 0.5 0.4
, textColour: Color.rgb 0 0 1
}
, language: "purescript"
, children: s'
}
Hole maxLength text, true ->
element segment
{ maxLength
, theKey: show i <> "," <> show j
, update: update <<< updateSegments i j
, text
, focusOnFirstRender: (firstHoleIndex <#> \fh -> fh.i == i && fh.j == j) # fromMaybe false
}
_, _ -> mempty
renderSegment i j s = case s, between start end i of
Filler s', true ->
element
syntaxHighlighter
{ style:
mkHighlighterTheme
{ grey: Color.rgb 50 50 50
, highlightColour: Color.hsl 30.0 0.5 0.4
, textColour: Color.rgb 0 0 1
}
, language: "purescript"
, children: s'
}
Hole maxLength text, true ->
element segment
{ maxLength
, theKey: show i <> "," <> show j
, update: update <<< updateSegments i j
, text
, focusOnFirstRender: (firstHoleIndex <#> \fh -> fh.i == i && fh.j == j) # fromMaybe false
}
_, _ -> mempty
type Props =
{ segments ∷ Array (Array Segment)
@ -109,7 +103,6 @@ makeComponent ∷ Effect (ReactComponent Props)
makeComponent = do
segment <- mkSegment
reactComponent "FillInTheGaps" \(props@{ updateSegments, segments, run, solvedWith } ∷ Props) -> React.do
ref <- useFocus
pure
$ R.form
{ onSubmit: handler preventDefault (const run)
@ -128,7 +121,6 @@ makeComponent = do
[ el Block.button
{ onClick: handler_ mempty
, buttonType: ButtonType.Primary
, ref: if isJust (findFirstHoleIndex segments) then unsafeCoerce undefined else ref
}
[ R.text "Run" ]
]

View File

@ -82,6 +82,7 @@ exports.colorizeImpl = function (text) {
};
};
exports.setValue = function (value) { return function (editor) { return function () { return editor.setValue(value); }; }; };
exports.layout = (editor) => () => editor.layout()
exports.defineThemeImpl = function (monacoInstance) {
return function (name) {
return function (theme) {
@ -114,99 +115,138 @@ exports.setMonarchTokensProviderImpl = function (monacoInstance) {
};
};
};
exports.vsCodeTheme = function (bg) {
exports.horizonTheme = function (bg) {
return ({
base: "vs",
inherit: true,
rules: [
{
foreground: "008e00",
token: "comment",
foreground: "#33333380",
fontStyle: "italic",
token: "comment"
},
{
foreground: "7d4726",
token: "meta.preprocessor",
foreground: "#DC3318",
token: "constant"
},
{
foreground: "7d4726",
token: "keyword.control.import",
foreground: "#F77D26",
token: "entity.name"
},
{
foreground: "df0002",
token: "string",
foreground: "#1D8991",
token: "entity.name.function"
},
{
foreground: "3a00dc",
token: "constant.numeric",
foreground: "#DA103F",
token: "entity.name.tag"
},
{
foreground: "c800a4",
token: "constant.language",
foreground: "#F77D26",
token: "entity.name.type"
},
{
foreground: "275a5e",
token: "constant.character",
foreground: "#DA103F",
token: "variable"
},
{
foreground: "275a5e",
token: "constant.other",
foreground: "#DA103F",
token: "entity.name.variable"
},
{
foreground: "c800a4",
token: "variable.language",
foreground: "#8A31B9",
token: "keyword"
},
{
foreground: "c800a4",
token: "variable.other",
foreground: "#333333",
token: "keyword.operator"
},
{
foreground: "c800a4",
token: "keyword",
foreground: "#8A31B9",
token: "keyword.operator.new"
},
{
foreground: "c900a4",
token: "storage",
foreground: "#8A31B9",
token: "keyword.operator.expression"
},
{
foreground: "438288",
token: "entity.name.class",
foreground: "#8A31B9",
token: "keyword.operator.logical, keyword.operator.delete"
},
{
foreground: "790ead",
token: "entity.name.tag",
foreground: "#8A31B9",
token: "keyword.operator.delete"
},
{
foreground: "450084",
token: "entity.other.attribute-name",
foreground: "#DC3318",
token: "keyword.other.unit"
},
{
foreground: "450084",
token: "support.function",
foreground: "#F6661EB3",
fontStyle: "italic",
token: "markup.quote"
},
{
foreground: "450084",
token: "support.constant",
foreground: "#DA103F",
token: "entity.name.section"
},
{
foreground: "790ead",
token: "support.type",
foreground: "#8A31B9",
token: "storage"
},
{
foreground: "790ead",
token: "support.class",
foreground: "#F6661E",
token: "string"
},
{
foreground: "790ead",
token: "support.other.variable",
foreground: "#F77D26",
token: "support"
},
{
foreground: "#1D8991",
token: "support.function"
},
{
foreground: "#DA103F",
token: "support.variable"
},
{
foreground: "#DA103F",
token: "support.type.property-name"
},
{
foreground: "#DA103F",
token: "meta.object-literal.key"
},
{
foreground: "#333333",
token: "support.type.property-name.css"
},
{
foreground: "#F77D26",
fontStyle: "italic",
token: "variable.language"
},
{
fontStyle: "italic",
token: "variable.parameter"
},
{
foreground: "#DA103FB3",
token: "punctuation.definition.tag"
},
{
foreground: "#333333",
token: "punctuation.separator"
},
],
colors: {
"editor.foreground": "#000000",
"editor.background": bg,
"editor.selectionBackground": "#B5D5FF",
"editor.background": bg, //"#FDF0ED"
"editor.selectionBackground": "#F9CBBE80",
"editor.lineHighlightBackground": "#B5D5FF",
"editor.lineHighlightBorder": "#B5D5FF",
"editorCursor.foreground": "#000000",
"editorCursor.foreground": "#333333",
"editorWhitespace.foreground": "#BFBFBF",
},
});
@ -218,6 +258,7 @@ exports.nightOwlTheme = function (bg) {
rules: [
{
foreground: "637777",
fontStyle: "italic",
token: "comment",
},
{

View File

@ -1,13 +1,15 @@
module Yoga.Editor where
import Prelude
import CSS (borderBox, borderRadius, boxSizing, calc, margin, padding, pct, px, toHexString, unitless, width)
import CSS (borderBox, borderRadius, boxSizing, margin, padding, pct, px, unitless, width)
import CSS.Overflow (hidden, overflowY)
import Control.Promise (Promise)
import Control.Promise as Promise
import Data.Foldable (for_)
import Data.Foldable (fold, foldMap, for_)
import Data.Maybe (Maybe(..), maybe)
import Data.Nullable (Nullable)
import Data.Monoid (guard)
import Data.Nullable (Nullable, notNull, null)
import Data.Time.Duration (Seconds(..))
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Aff (Aff)
@ -19,16 +21,14 @@ import JSS (jssClasses)
import Prim.Row (class Union)
import React.Basic (JSX, ReactComponent, Ref, element, fragment)
import React.Basic.DOM as R
import React.Basic.Emotion (var)
import React.Basic.Hooks (reactComponent, useEffect, useState)
import React.Basic.Hooks (reactComponent, useEffect, useEffectAlways, useState)
import React.Basic.Hooks as React
import React.Basic.Hooks.Aff (useAff)
import Web.DOM (Node)
import Web.HTML (HTMLElement)
import Yoga.Theme (fromTheme)
import Yoga.Theme.Default (darkTheme)
import Yoga.Block.Container.Style (DarkOrLightMode(..), getDarkOrLightMode)
import Yoga.Block.Hook.UseResize (useOnResize)
import Yoga.Theme.Styles (makeStylesJSS)
import Yoga.Theme.Types (CSSTheme)
type EditorProps =
( value ∷ String
@ -52,7 +52,7 @@ foreign import setThemeImpl ∷ Monaco -> String -> Effect Unit
foreign import nightOwlTheme ∷ String -> MonacoTheme
foreign import vsCodeTheme ∷ String -> MonacoTheme
foreign import horizonTheme ∷ String -> MonacoTheme
foreign import getValue ∷ Editor -> Effect String
@ -60,6 +60,8 @@ foreign import setValue ∷ String -> Editor -> Effect Unit
foreign import colorizeImpl ∷ String -> String -> { tabSize ∷ Int } -> Editor -> Promise String
foreign import layout ∷ Editor -> Effect Unit
foreign import data Monaco ∷ Type
foreign import data Editor ∷ Type
@ -75,7 +77,7 @@ darkThemeName ∷ String
darkThemeName = "NightOwl"
lightThemeName ∷ String
lightThemeName = "VSCode"
lightThemeName = "Horizon"
foreign import data MonarchLanguage ∷ Type
@ -85,10 +87,10 @@ foreign import registerLanguageImpl ∷ Monaco -> String -> Effect Unit
foreign import setMonarchTokensProviderImpl ∷ Monaco -> String -> MonarchLanguage -> Effect Unit
initEditor ∷ CSSTheme -> Monaco -> Effect Unit
initEditor theme monaco = do
defineThemeImpl monaco darkThemeName (nightOwlTheme (toHexString theme.backgroundColour))
defineThemeImpl monaco lightThemeName (vsCodeTheme (toHexString theme.backgroundColour))
initEditor ∷ Monaco -> Effect Unit
initEditor monaco = do
defineThemeImpl monaco darkThemeName (nightOwlTheme "#212134") -- [TODO] Read from somewhere else
defineThemeImpl monaco lightThemeName (horizonTheme "#EBDFDE") -- [TODO] Don't hardcode
registerLanguageImpl monaco "purescript"
setMonarchTokensProviderImpl monaco "purescript" purescriptSyntax
@ -111,26 +113,35 @@ mkEditor = do
padding (4.0 # px) (8.0 # px) (0.0 # px) (8.0 # px)
width (100.0 # pct)
overflowY hidden
-- backgroundColor (colour.background) [TODO]
-- backgroundColor (colour.background) [ TODO ]
}
reactComponent "Editor" \{ onLoad, height, language } -> React.do
classes <- useStyles {}
maybeEditor /\ modifyEditor <- useState Nothing
maybeMonaco /\ modifyMonaco <- useState Nothing
mbEditorComponent /\ setEditorComponent <- React.useState' Nothing
monacoRef <- React.useRef null
editorRef <- React.useRef null
useOnResize (3.0 # Seconds) \_ -> do
mbEditor <- React.readRefMaybe editorRef
for_ mbEditor layout
useAff unit do
eddy <- monacoEditor
liftEffect $ modifyEditor (const (Just eddy))
useEffect unit do -- [TODO] notice theme change
for_ maybeMonaco (initEditor (fromTheme darkTheme))
pure mempty
med <- monacoEditor
setEditorComponent (Just med) # liftEffect
useEffectAlways do
mbMode <- getDarkOrLightMode
mbMonaco <- React.readRefMaybe monacoRef
case mbMode, mbMonaco of
Just DarkMode, Just monaco -> setThemeImpl monaco darkThemeName
Just LightMode, Just monaco -> setThemeImpl monaco lightThemeName
_, _ -> mempty
mempty
let themeName = darkThemeName
pure
$ fragment
[ R.div
{ className: classes.wrapper
, children:
[ maybeEditor
# maybe mempty \editor ->
[ mbEditorComponent
# foldMap \editor ->
element editor
{ theme: themeName
, height
@ -154,11 +165,14 @@ mkEditor = do
}
, language
-- https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-custom-languages
, editorDidMount: mkEffectFn2 \e _ -> onLoad e
, editorDidMount:
mkEffectFn2 \e _ -> do
React.writeRef editorRef (notNull e)
onLoad e
, editorWillMount:
mkEffectFn1 \m -> do
modifyMonaco (const $ Just m)
(initEditor (fromTheme darkTheme) m)
React.writeRef monacoRef (notNull m)
initEditor m
}
]
}