diff --git a/bower.json b/bower.json index 4b4a8f3..be354bc 100644 --- a/bower.json +++ b/bower.json @@ -15,8 +15,8 @@ "package.json" ], "dependencies": { - "purescript-halogen": "^0.12.0", - "purescript-halogen-css": "^4.0.0", + "purescript-halogen": "^1.0.0-rc.2", + "purescript-halogen-css": "^5.0.0-rc.1", "purescript-echarts": "^3.0.0" }, "devDependencies": { diff --git a/example/src/Main.purs b/example/src/Main.purs index 465a7fa..ce4c220 100644 --- a/example/src/Main.purs +++ b/example/src/Main.purs @@ -4,18 +4,20 @@ import Prelude import Control.Monad.Aff (Aff) import Control.Monad.Eff (Eff) +import Control.Monad.Eff.Class (liftEff) import Control.Monad.Eff.Random (randomInt, RANDOM) import Data.Array ((!!), length, snoc, sort, reverse, head, filter) -import Data.Functor.Coproduct (Coproduct) +import Data.Foldable (for_) import Data.Maybe (Maybe(..), maybe) +import Data.Tuple.Nested ((/\)) import Halogen as H import Halogen.ECharts as EC -import Halogen.HTML.Events.Indexed as HE -import Halogen.HTML.Indexed as HH -import Halogen.HTML.Properties.Indexed as HP -import Halogen.Util (runHalogenAff, awaitBody) +import Halogen.HTML.Events as HE +import Halogen.HTML as HH +import Halogen.Aff (runHalogenAff, awaitBody) +import Halogen.VDom.Driver (runUI) import Options (options) @@ -28,30 +30,31 @@ type State = { arr ∷ Array Int } -initialState ∷ State -initialState = { arr: [ ] } +initialState ∷ ∀ a. a → State +initialState _ = { arr: [ ] } data Query a = SetRandomOption Int a | AddChart a | RemoveChart Int a + | HandleEChartsMessage Int EC.EChartsMessage a type Slot = Int -type StateP = H.ParentState State EC.EChartsState Query EC.EChartsQuery AffCharts Slot -type QueryP = Coproduct Query (H.ChildF Slot EC.EChartsQuery) - -type AppEffects = EC.EChartsEffects - ( random ∷ RANDOM - ) +type AppEffects = EC.EChartsEffects ( random ∷ RANDOM ) type AffCharts = Aff AppEffects -type HTML = H.ParentHTML EC.EChartsState Query EC.EChartsQuery AffCharts Slot -type DSL = H.ParentDSL State EC.EChartsState Query EC.EChartsQuery AffCharts Slot +type HTML = H.ParentHTML Query EC.EChartsQuery Int AffCharts +type DSL = H.ParentDSL State Query EC.EChartsQuery Int Void AffCharts -comp ∷ H.Component StateP QueryP AffCharts -comp = H.parentComponent { render, eval, peek: Just peek } +comp ∷ H.Component HH.HTML Query Unit Void AffCharts +comp = H.parentComponent + { initialState + , render + , eval + , receiver: const Nothing + } render ∷ State → HTML render state = @@ -64,13 +67,10 @@ render state = <> map renderOne state.arr where renderOne ix = - HH.div - [ HP.key ("echarts-" <> show ix) ] + HH.div_ [ HH.div_ - [ HH.slot ix \_ → - { component: EC.echarts - , initialState: EC.initialEChartsState 400 300 - } + [ HH.slot ix EC.echarts ({width: 400, height: 300} /\ unit) + (Just <<< H.action <<< HandleEChartsMessage ix) ] , HH.button [ HE.onClick (HE.input_ (SetRandomOption ix)) ] @@ -82,10 +82,9 @@ render state = eval ∷ Query ~> DSL eval (SetRandomOption ix next) = do - mbopt ← H.fromEff $ randomInArray options - case mbopt of - Nothing → pure unit - Just opt → void $ H.query ix $ H.action (EC.Reset opt) + mbopt ← liftEff $ randomInArray options + for_ mbopt \opt → + void $ H.query ix $ H.action $ EC.Reset opt pure next eval (AddChart next) = do H.modify (\x → x{arr = snoc x.arr (maybe 0 (add one) $ head $ reverse $ sort x.arr)}) @@ -93,14 +92,13 @@ eval (AddChart next) = do eval (RemoveChart ix next) = do H.modify (\x → x{arr = filter (_ /= ix) x.arr}) pure next - -peek ∷ ∀ x. H.ChildF Int EC.EChartsQuery x → DSL Unit -peek (H.ChildF ix (EC.Init _)) = do - mbopt ← H.fromEff $ randomInArray options - case mbopt of - Nothing → pure unit - Just opt → void $ H.query ix $ H.action (EC.Set opt) -peek _ = pure unit +eval (HandleEChartsMessage ix EC.Initialized next) = do + mbopt ← liftEff $ randomInArray options + for_ mbopt \opt → + void $ H.query ix $ H.action $ EC.Set opt + pure next main ∷ Eff AppEffects Unit -main = runHalogenAff $ H.runUI comp (H.parentState initialState) =<< awaitBody +main = runHalogenAff do + body ← awaitBody + runUI comp unit body diff --git a/package.json b/package.json index e69ae50..c993287 100644 --- a/package.json +++ b/package.json @@ -2,16 +2,15 @@ "private": true, "scripts": { "clean": "rm -rf output .pulp-cache example/dist/test.js", - "build": "pulp build --censor-lib --strict", + "build": "pulp build -- --censor-lib --strict", "example": "npm run clean && pulp browserify --include example/src --to example/dist/test.js" }, "dependencies": { - "pulp": "^9.0.1", - "purescript": "^0.10.1", - "purescript-psa": "^0.3.9" + "pulp": "^10.0.0", + "purescript": "^0.10.5", + "purescript-psa": "^0.4.0" }, "devDependencies": { - "virtual-dom": "^2.1.1", - "echarts": "^3.3.1" + "echarts": "^3.4.0" } } diff --git a/src/Halogen/ECharts.purs b/src/Halogen/ECharts.purs index 232fb50..5cb511d 100644 --- a/src/Halogen/ECharts.purs +++ b/src/Halogen/ECharts.purs @@ -1,26 +1,28 @@ module Halogen.ECharts ( echarts + , echarts' , EChartsState , EChartsQuery(..) - , initialEChartsState + , EChartsMessage(..) , EChartsEffects ) where import Prelude -import Control.Monad.Eff (Eff) +import Control.Monad.Eff.Class (liftEff) import Control.Monad.Eff.Exception (EXCEPTION) +import Control.Monad.Eff.Ref (REF) import Control.Monad.Aff.AVar (AVAR) -import Control.Monad.Aff.Free (class Affable) +import Control.Monad.Aff.Class (class MonadAff) -import Data.Foldable (for_) +import Data.Foldable (for_, traverse_) import Data.Foreign (Foreign) import Data.Int (toNumber) import Data.Maybe (Maybe(..)) import Data.Traversable (for) +import Data.Tuple.Nested (type (/\), (/\)) import DOM (DOM) -import DOM.HTML.Types (HTMLElement) import CSS.Geometry (width, height) import CSS.Size (px) @@ -31,65 +33,73 @@ import ECharts.Monad as EM import ECharts.Types.Phantom as ETP import Halogen as H -import Halogen.HTML.CSS.Indexed (style) -import Halogen.HTML.Indexed as HH -import Halogen.HTML.Properties.Indexed as HP +import Halogen.HTML.CSS (style) +import Halogen.HTML as HH +import Halogen.HTML.Properties as HP + type EChartsState = - { element ∷ Maybe HTMLElement - , chart ∷ Maybe ET.Chart + { chart ∷ Maybe ET.Chart , width ∷ Int , height ∷ Int } -initialEChartsState ∷ Int → Int → EChartsState -initialEChartsState w h = - { element: Nothing - , chart: Nothing - , width: w - , height: h - } - data EChartsQuery a - = SetElement (Maybe HTMLElement) a - | Init a + = Init a | Dispose a | Set (EM.DSL ETP.OptionI) a | Reset (EM.DSL ETP.OptionI) a | Resize a | Clear a - | SetHeight Int a - | SetWidth Int a + | SetDimensions { width ∷ Maybe Int, height ∷ Maybe Int } a | GetOptions (Maybe Foreign → a) | GetWidth (Int → a) | GetHeight (Int → a) +data EChartsMessage = Initialized + type EChartsEffects eff = ( echarts ∷ ET.ECHARTS , dom ∷ DOM , avar ∷ AVAR , err ∷ EXCEPTION + , ref ∷ REF | eff ) +type Dimensions = { width ∷ Int, height ∷ Int } + type HTML = H.ComponentHTML EChartsQuery -type DSL g = H.ComponentDSL EChartsState EChartsQuery g + +type DSL g = H.ComponentDSL EChartsState EChartsQuery EChartsMessage g echarts ∷ ∀ eff g - . (Affable (EChartsEffects eff) g) - ⇒ H.Component EChartsState EChartsQuery g -echarts = H.lifecycleComponent - { render + . (MonadAff (EChartsEffects eff) g) + ⇒ H.Component HH.HTML EChartsQuery (Dimensions /\ Unit) EChartsMessage g +echarts = + echarts' \(dim /\ _) → + Just $ H.action $ SetDimensions { width: Just dim.width, height: Just dim.height } + +echarts' + ∷ ∀ eff g i + . (MonadAff (EChartsEffects eff) g) + ⇒ (Dimensions /\ i → Maybe (EChartsQuery Unit)) + → H.Component HH.HTML EChartsQuery (Dimensions /\ i) EChartsMessage g +echarts' receiver = H.lifecycleComponent + { initialState: \({width, height} /\ _) → { width, height, chart: Nothing } + , render , eval , initializer: Just (H.action Init) , finalizer: Nothing + , receiver } -render ∷ EChartsState → HTML +render + ∷ EChartsState → HTML render state = HH.div - [ HP.ref (H.action <<< SetElement) + [ HP.ref $ H.RefLabel "echarts" , style do height $ px $ toNumber state.height width $ px $ toNumber state.width @@ -98,58 +108,46 @@ render state = eval ∷ ∀ eff g - . (Affable (EChartsEffects eff) g) + . (MonadAff (EChartsEffects eff) g) ⇒ EChartsQuery ~> (DSL g) -eval (SetElement el next) = do - H.modify (_ { element = el }) - pure next eval (Init next) = do - state ← H.get - for_ state.element \el → do - chart ← H.fromEff (EC.init el ∷ Eff (EChartsEffects eff) ET.Chart) - H.modify (_{chart = pure chart}) + H.getHTMLElementRef (H.RefLabel "echarts") + >>= traverse_ \el → do + chart ← liftEff $ EC.init el + H.modify _{ chart = Just chart } + H.raise Initialized pure next eval (Dispose next) = do state ← H.get - for_ state.chart \chart → do - H.fromEff (EC.dispose chart ∷ Eff (EChartsEffects eff) Unit) + for_ state.chart $ liftEff <<< EC.dispose pure next eval (Set opts next) = do state ← H.get - for_ state.chart \chart → do - H.fromEff (EC.setOption opts chart ∷ Eff (EChartsEffects eff) Unit) + for_ state.chart $ liftEff <<< EC.setOption opts pure next eval (Reset opts next) = do state ← H.get - for_ state.chart \chart → do - H.fromEff (EC.resetOption opts chart ∷ Eff (EChartsEffects eff) Unit) + for_ state.chart $ liftEff <<< EC.resetOption opts pure next eval (Resize next) = do state ← H.get - for_ state.chart \chart → - H.fromEff (EC.resize chart ∷ Eff (EChartsEffects eff) Unit) + for_ state.chart $ liftEff <<< EC.resize pure next eval (Clear next) = do state ← H.get - for_ state.chart \chart → - H.fromEff (EC.clear chart ∷ Eff (EChartsEffects eff) Unit) - pure next -eval (SetHeight h next) = do - H.modify (_{height = h}) - state ← H.get - for_ state.chart \chart → - H.fromEff (EC.resize chart ∷ Eff (EChartsEffects eff) Unit) + for_ state.chart $ liftEff <<< EC.clear pure next -eval (SetWidth w next) = do - H.modify (_{width = w}) +eval (SetDimensions { width, height } next) = do + for_ width \w → do + H.modify _{ width = w } + for_ height \h → do + H.modify _{ height = h } state ← H.get - for_ state.chart \chart → - H.fromEff (EC.resize chart ∷ Eff (EChartsEffects eff) Unit) + for_ state.chart $ liftEff <<< EC.resize pure next eval (GetOptions continue) = do state ← H.get - mbOptions ← for state.chart \chart → - H.fromEff (EC.getOption chart ∷ Eff (EChartsEffects eff) Foreign) + mbOptions ← for state.chart $ liftEff <<< EC.getOption pure $ continue mbOptions eval (GetWidth continue) = do map continue $ H.gets _.width