mirror of
https://github.com/hariroshan/elm-native-library.git
synced 2025-01-06 03:54:33 +03:00
runs in ios
This commit is contained in:
parent
12850e2add
commit
28f82ad2a8
7
elm.json
7
elm.json
@ -1,17 +1,18 @@
|
||||
{
|
||||
"type": "application",
|
||||
"source-directories": [
|
||||
"src"
|
||||
"src",
|
||||
"nativescript-bind-elm/src"
|
||||
],
|
||||
"elm-version": "0.19.1",
|
||||
"dependencies": {
|
||||
"direct": {
|
||||
"elm/browser": "1.0.2",
|
||||
"elm/core": "1.0.5",
|
||||
"elm/html": "1.0.0"
|
||||
"elm/html": "1.0.0",
|
||||
"elm/json": "1.1.3"
|
||||
},
|
||||
"indirect": {
|
||||
"elm/json": "1.1.3",
|
||||
"elm/time": "1.0.0",
|
||||
"elm/url": "1.0.0",
|
||||
"elm/virtual-dom": "1.0.3"
|
||||
|
@ -1,13 +1,22 @@
|
||||
{
|
||||
"type": "package",
|
||||
"name": "hariroshan/elm-native",
|
||||
"summary": "Nativescript bindings to build mobile apps using elm",
|
||||
"license": "BSD-3-Clause",
|
||||
"version": "0.1.0",
|
||||
"exposed-modules": [],
|
||||
"elm-version": "0.19.0 <= v < 2.0.0",
|
||||
"dependencies": {
|
||||
"elm/core": "1.0.0 <= v < 2.0.0"
|
||||
},
|
||||
"test-dependencies": {}
|
||||
"type": "package",
|
||||
"name": "hariroshan/elm-native",
|
||||
"summary": "Nativescript bindings to build mobile apps using elm",
|
||||
"license": "BSD-3-Clause",
|
||||
"version": "0.1.0",
|
||||
"exposed-modules": [
|
||||
"Native",
|
||||
"Native.Attributes",
|
||||
"Native.Frame",
|
||||
"Native.Page",
|
||||
"Native.Layout",
|
||||
"Native.Event"
|
||||
],
|
||||
"elm-version": "0.19.0 <= v < 2.0.0",
|
||||
"dependencies": {
|
||||
"elm/core": "1.0.0 <= v < 2.0.0",
|
||||
"elm/html": "1.0.0 <= v < 2.0.0",
|
||||
"elm/json": "1.1.3 <= v < 2.0.0"
|
||||
},
|
||||
"test-dependencies": {}
|
||||
}
|
||||
|
@ -1,5 +1,12 @@
|
||||
module Native exposing (Native)
|
||||
module Native exposing (label)
|
||||
|
||||
import Html exposing (Attribute, Html)
|
||||
|
||||
|
||||
type Native
|
||||
= Native
|
||||
|
||||
label : List (Attribute msg) -> List (Html msg) -> Html msg
|
||||
label attrs children =
|
||||
Html.node "ns-label" attrs children
|
||||
|
||||
|
9
nativescript-bind-elm/src/Native/Attributes.elm
Normal file
9
nativescript-bind-elm/src/Native/Attributes.elm
Normal file
@ -0,0 +1,9 @@
|
||||
module Native.Attributes exposing (text)
|
||||
|
||||
import Html exposing (Attribute)
|
||||
import Html.Attributes exposing (attribute)
|
||||
|
||||
|
||||
text : String -> Attribute msg
|
||||
text =
|
||||
attribute "text"
|
10
nativescript-bind-elm/src/Native/Event.elm
Normal file
10
nativescript-bind-elm/src/Native/Event.elm
Normal file
@ -0,0 +1,10 @@
|
||||
module Native.Event exposing (on)
|
||||
|
||||
import Html exposing (Attribute)
|
||||
import Html.Events as Event
|
||||
import Json.Decode as D
|
||||
|
||||
|
||||
on : String -> D.Decoder msg -> Attribute msg
|
||||
on eventName =
|
||||
Event.on eventName
|
74
nativescript-bind-elm/src/Native/Frame.elm
Normal file
74
nativescript-bind-elm/src/Native/Frame.elm
Normal file
@ -0,0 +1,74 @@
|
||||
module Native.Frame exposing (Frame, asElement, frame, root)
|
||||
|
||||
import Html exposing (Attribute, Html)
|
||||
import Native.Page as Page exposing (Page)
|
||||
|
||||
|
||||
type Frame msg
|
||||
= Frame (Html msg)
|
||||
|
||||
|
||||
frame : { a | next : Maybe page, history : List page } -> List ( page, { a | next : Maybe page, history : List page } -> Page msg ) -> List (Attribute msg) -> Frame msg
|
||||
frame model pages attrs =
|
||||
Frame
|
||||
(Html.node "ns-frame"
|
||||
attrs
|
||||
(model.history
|
||||
|> List.foldl
|
||||
(\next acc ->
|
||||
pages
|
||||
|> getPage next model
|
||||
|> Maybe.map (\x -> x :: acc)
|
||||
|> Maybe.withDefault acc
|
||||
)
|
||||
[]
|
||||
)
|
||||
-- ([ pages
|
||||
-- |> getPage model.current model
|
||||
-- , model.next
|
||||
-- |> Maybe.map
|
||||
-- (\next ->
|
||||
-- pages
|
||||
-- |> getPage next model
|
||||
-- )
|
||||
-- |> Maybe.withDefault []
|
||||
-- ]
|
||||
-- |> List.concat
|
||||
-- )
|
||||
)
|
||||
|
||||
|
||||
getPage : a -> b -> List ( a, b -> Page msg ) -> Maybe (Html msg)
|
||||
getPage target model pages =
|
||||
pages
|
||||
|> findInList target Tuple.first Nothing
|
||||
|> Maybe.map
|
||||
(\( _, fx ) ->
|
||||
model
|
||||
|> fx
|
||||
|> Page.unwrap
|
||||
)
|
||||
|
||||
|
||||
findInList : b -> (a -> b) -> Maybe a -> List a -> Maybe a
|
||||
findInList target toItem acc ls =
|
||||
case ls of
|
||||
[] ->
|
||||
acc
|
||||
|
||||
h :: r ->
|
||||
if target == toItem h then
|
||||
Just h
|
||||
|
||||
else
|
||||
findInList target toItem acc r
|
||||
|
||||
|
||||
root : Frame msg -> Html msg
|
||||
root (Frame e) =
|
||||
e
|
||||
|
||||
|
||||
asElement : Frame msg -> Html msg
|
||||
asElement =
|
||||
root
|
17
nativescript-bind-elm/src/Native/Layout.elm
Normal file
17
nativescript-bind-elm/src/Native/Layout.elm
Normal file
@ -0,0 +1,17 @@
|
||||
module Native.Layout exposing (Layout, asElement, rootLayout)
|
||||
|
||||
import Html exposing (Attribute, Html)
|
||||
|
||||
|
||||
type Layout msg
|
||||
= Layout (Html msg)
|
||||
|
||||
|
||||
rootLayout : List (Attribute msg) -> List (Html msg) -> Layout msg
|
||||
rootLayout attrs children =
|
||||
Layout (Html.node "ns-root-layout" attrs children)
|
||||
|
||||
|
||||
asElement : Layout msg -> Html msg
|
||||
asElement (Layout e) =
|
||||
e
|
22
nativescript-bind-elm/src/Native/Page.elm
Normal file
22
nativescript-bind-elm/src/Native/Page.elm
Normal file
@ -0,0 +1,22 @@
|
||||
module Native.Page exposing (Page, page, unwrap)
|
||||
|
||||
import Html exposing (Attribute, Html)
|
||||
import Native.Layout as Layout exposing (Layout)
|
||||
|
||||
|
||||
type Page msg
|
||||
= Page (Html msg)
|
||||
|
||||
|
||||
page : List (Attribute msg) -> Layout msg -> Page msg
|
||||
page attrs layout =
|
||||
Page
|
||||
(Html.node "ns-page"
|
||||
attrs
|
||||
[ Layout.asElement layout ]
|
||||
)
|
||||
|
||||
|
||||
unwrap : Page msg -> Html msg
|
||||
unwrap (Page e) =
|
||||
e
|
@ -9,7 +9,7 @@ type rec context = {
|
||||
elm: unit => elmModule,
|
||||
flags: Js.Nullable.t<Obj.t>,
|
||||
initPorts: Js.Nullable.t<Obj.t => unit>,
|
||||
createFunctions: Obj.t => Obj.t,
|
||||
withCustomElements: (. Obj.t, Types.handler) => Obj.t,
|
||||
elements: array<Types.customElement>,
|
||||
}
|
||||
|
||||
@ -20,16 +20,15 @@ type rec context = {
|
||||
external setGlobalDocument: (global, Dom.document) => unit = "document"
|
||||
|
||||
let getRootLayout: unit => NativescriptCore.rootLayout = _ =>
|
||||
%raw(`document.body.children[0].object`)
|
||||
%raw(`document.body.children[0].data`)
|
||||
|
||||
let initElements = (params: context) => {
|
||||
let htmlElement = params.window->Mock.hTMLElement
|
||||
let customElements = params.window->Mock.customElements
|
||||
|
||||
htmlElement->params.createFunctions->ignore
|
||||
|
||||
params.elements->Belt.Array.forEach(element => {
|
||||
element.tagName->customElements.define(htmlElement->element.make)
|
||||
let newClass = params.withCustomElements(. htmlElement, element.handler)
|
||||
customElements.define(. element.tagName, newClass)
|
||||
})
|
||||
|
||||
NativescriptCore.Application.run({
|
||||
@ -60,10 +59,10 @@ let start: config => unit = config => {
|
||||
initPorts: config.initPorts,
|
||||
elm: config.elmModule,
|
||||
elements: Native.allElements,
|
||||
createFunctions: CustomElement.createFunctions,
|
||||
withCustomElements: CustomElement.withCustomElements,
|
||||
}
|
||||
|
||||
let defineCustomElements = `initElements({window, createFunctions, elements})`
|
||||
let defineCustomElements = `initElements({window, withCustomElements, elements})`
|
||||
let elmRoot = "elm-root"
|
||||
|
||||
let elmInitScript = `
|
||||
|
@ -1,163 +1,2 @@
|
||||
type constructor = {
|
||||
observedAttributes: array<string>,
|
||||
name: string,
|
||||
}
|
||||
|
||||
type event
|
||||
|
||||
type nativeObject = {
|
||||
on: (event, event => unit) => unit,
|
||||
off: (event, event => unit) => unit,
|
||||
}
|
||||
|
||||
type super = {
|
||||
addEventListener: (event, event => unit) => unit,
|
||||
removeEventListener: (event, event => unit) => unit,
|
||||
}
|
||||
|
||||
type extendedThis = {
|
||||
getAttributes: unit => Js.Dict.t<string>,
|
||||
getProps: unit => Js.Dict.t<string>,
|
||||
init: unit => unit,
|
||||
update: (string, string) => unit,
|
||||
dispose: unit => unit,
|
||||
isConnected: bool,
|
||||
render: Js.Nullable.t<unit => unit>,
|
||||
object: nativeObject,
|
||||
}
|
||||
|
||||
type this = {
|
||||
getAttribute: string => Js.Nullable.t<string>,
|
||||
style: string,
|
||||
super: super,
|
||||
constructor: constructor,
|
||||
}
|
||||
|
||||
%%private(
|
||||
@scope("prototype") @set
|
||||
external assignGetPropsInProto: (Obj.t, unit => Js.Dict.t<string>) => unit = "getProps"
|
||||
|
||||
@scope("prototype") @set
|
||||
external assignGetAttributes: (Obj.t, unit => Js.Dict.t<string>) => unit = "getAttributes"
|
||||
|
||||
@scope("prototype") @set
|
||||
external assignConstructor: (Obj.t, unit => unit) => unit = "constructor"
|
||||
|
||||
@scope("prototype") @set
|
||||
external assignAttributeChangedCallback: (Obj.t, (string, unit, string) => unit) => unit =
|
||||
"attributeChangedCallback"
|
||||
|
||||
@scope("prototype") @set
|
||||
external assignConnectedCallback: (Obj.t, unit => unit) => unit = "connectedCallback"
|
||||
|
||||
@scope("prototype") @set
|
||||
external assignDisconnectedCallback: (Obj.t, unit => unit) => unit = "disconnectedCallback"
|
||||
|
||||
@scope("prototype") @set
|
||||
external assignAddEventListener: (Obj.t, (event, event => unit) => unit) => unit =
|
||||
"addEventListener"
|
||||
|
||||
@scope("prototype") @set
|
||||
external assignRemoveEventListener: (Obj.t, (event, event => unit) => unit) => unit =
|
||||
"removeEventListener"
|
||||
|
||||
@set
|
||||
external assignObservedAttributes: (Obj.t, unit => array<string>) => unit = "observedAttributes"
|
||||
|
||||
@val external this: this = "this"
|
||||
|
||||
external toExtendedThis: this => extendedThis = "%identity"
|
||||
|
||||
@val external thisSuper: unit => unit = "this.super"
|
||||
|
||||
@set
|
||||
external setThisProps: (this, Js.Dict.t<string>) => unit = "props"
|
||||
|
||||
let withAttrs = class => {
|
||||
class->assignObservedAttributes(_ => [])
|
||||
class->assignGetAttributes(_ => {
|
||||
this.constructor.observedAttributes->Belt.Array.reduce(Js.Dict.empty(), (acc, attrName) => {
|
||||
let attrValue = switch this.getAttribute(attrName)->Js.Nullable.toOption {
|
||||
| Some(a) => Some(a)
|
||||
| None => attrName == "style" ? Some(this.style) : None
|
||||
}
|
||||
let attrNameMap = attrName == "class" ? "className" : attrName
|
||||
|
||||
attrValue
|
||||
->Belt.Option.map(
|
||||
value => {
|
||||
acc->Js.Dict.set(attrNameMap, value)
|
||||
acc
|
||||
},
|
||||
)
|
||||
->Belt.Option.getWithDefault(acc)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let withProps = class => {
|
||||
class->assignGetPropsInProto(() => {
|
||||
(this->toExtendedThis).getAttributes()
|
||||
})
|
||||
}
|
||||
|
||||
let withCreate = class => {
|
||||
class->assignConstructor(() => {
|
||||
thisSuper()
|
||||
Js.log(this.constructor.name ++ " created")
|
||||
(this->toExtendedThis).init()
|
||||
})
|
||||
}
|
||||
|
||||
let withInitAndUpdate = class => {
|
||||
class->assignAttributeChangedCallback((name, _, value) => {
|
||||
let extendedThis = this->toExtendedThis
|
||||
this->setThisProps(extendedThis.getProps())
|
||||
extendedThis.update(name, value)
|
||||
Js.log(this.constructor.name ++ " update")
|
||||
})
|
||||
}
|
||||
|
||||
let withMountAndRender = class => {
|
||||
class->assignConnectedCallback(_ => {
|
||||
let extendedThis = this->toExtendedThis
|
||||
if extendedThis.isConnected {
|
||||
extendedThis.render->Js.Nullable.toOption->Belt.Option.forEach(fx => fx())
|
||||
}
|
||||
|
||||
Js.log(this.constructor.name ++ " connected")
|
||||
})
|
||||
}
|
||||
|
||||
let withUnmount = class => {
|
||||
class->assignDisconnectedCallback(_ => {
|
||||
(this->toExtendedThis).dispose()
|
||||
Js.log(this.constructor.name ++ " disconnected")
|
||||
})
|
||||
}
|
||||
|
||||
let withEventListener = class => {
|
||||
class->assignAddEventListener((event, callback) => {
|
||||
this.super.addEventListener(event, callback)
|
||||
(this->toExtendedThis).object.on(event, callback)
|
||||
})
|
||||
class->assignRemoveEventListener((event, callback) => {
|
||||
this.super.removeEventListener(event, callback)
|
||||
(this->toExtendedThis).object.off(event, callback)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
let createFunctions = class =>
|
||||
[
|
||||
withAttrs,
|
||||
withProps,
|
||||
withCreate,
|
||||
withInitAndUpdate,
|
||||
withMountAndRender,
|
||||
withUnmount,
|
||||
withEventListener,
|
||||
]->Belt.Array.reduce(class, (acc, cur) => {
|
||||
cur(acc)
|
||||
acc
|
||||
})
|
||||
@module("./custom")
|
||||
external withCustomElements: (. Obj.t, Types.handler) => Obj.t = "withCustomElements"
|
||||
|
@ -1,11 +1,30 @@
|
||||
module TextView = {
|
||||
let tagName = "ns-text-view"
|
||||
let make = class => class
|
||||
module Label = {
|
||||
%%private(
|
||||
@module("@nativescript/core") @new
|
||||
external new: unit => Types.nativeObject = "Label"
|
||||
let label = new()
|
||||
let attributes = Helper.getPropsForObject(Obj.magic(label))
|
||||
label.destroyNode(.)
|
||||
)
|
||||
let tagName = "ns-label"
|
||||
|
||||
let handler: Types.handler = {
|
||||
init: (. ()) => new(),
|
||||
observedAttributes: attributes,
|
||||
render: Js.Nullable.return((. current: Types.this, _) =>
|
||||
Helper.addView(. current.parentElement, current)
|
||||
),
|
||||
pageAdded: Js.Nullable.null,
|
||||
update: NativescriptCore.update,
|
||||
dispose: NativescriptCore.dispose,
|
||||
addEventListener: NativescriptCore.addEventListener,
|
||||
removeEventListener: NativescriptCore.removeEventListener,
|
||||
}
|
||||
}
|
||||
|
||||
let all: array<Types.customElement> = [
|
||||
{
|
||||
tagName: TextView.tagName,
|
||||
make: TextView.make,
|
||||
tagName: Label.tagName,
|
||||
handler: Label.handler,
|
||||
},
|
||||
]
|
||||
|
1
nativescript-bind-res/Native/Elements.resi
Normal file
1
nativescript-bind-res/Native/Elements.resi
Normal file
@ -0,0 +1 @@
|
||||
let all: array<Types.customElement>
|
73
nativescript-bind-res/Native/Helper.res
Normal file
73
nativescript-bind-res/Native/Helper.res
Normal file
@ -0,0 +1,73 @@
|
||||
let always = (x, _) => x
|
||||
|
||||
let rec assignDeep: (Obj.t, array<string>, int, 'a) => unit = (object, keys, i, value) => {
|
||||
let optionKey = keys->Belt.Array.get(i)
|
||||
optionKey
|
||||
->Belt.Option.flatMap(key => Js.Dict.get(Obj.magic(object), key)->Belt.Option.map(x => (key, x)))
|
||||
->Belt.Option.forEach(((key, v)) => {
|
||||
if keys->Array.length - 1 == i {
|
||||
Js.Dict.set(Obj.magic(object), key, value)
|
||||
} else {
|
||||
assignDeep(v, keys, i + 1, value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let setAttribute: (Obj.t, string, 'a) => unit = (object, key, value) => {
|
||||
if !(key->Js.String2.includes(".")) {
|
||||
Js.Dict.set(Obj.magic(object), key, value)
|
||||
} else {
|
||||
let keys = key->Js.String2.split(".")
|
||||
assignDeep(Obj.magic(object), keys, 0, value)
|
||||
}
|
||||
}
|
||||
|
||||
let init = (object, props) => {
|
||||
Js.Dict.keys(Obj.magic(props))->Belt.Array.forEach(key =>
|
||||
setAttribute(object, key, Js.Dict.unsafeGet(props, key))
|
||||
)
|
||||
}
|
||||
|
||||
let update = setAttribute
|
||||
|
||||
external typeOf: 'a => string = "typeof"
|
||||
|
||||
let getPropsForObject: Obj.t => array<string> = %raw(`
|
||||
function(object){
|
||||
let arr = []
|
||||
for (var prop in object) {
|
||||
if (prop.startsWith("_")) continue
|
||||
if (typeof object[prop] === "function") continue
|
||||
arr.push(prop)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
`)
|
||||
|
||||
let addView: (. 'a, 'b) => unit = %raw(`
|
||||
function(parentElement, thisElement) {
|
||||
requestAnimationFrame(() => {
|
||||
const children = Array.from(parentElement.children)
|
||||
const hasActionBar = children.some(x =>
|
||||
x.tagName.toLowerCase() === "ns-action-bar"
|
||||
)
|
||||
const index = (Array.from(parentElement.children).indexOf(thisElement))
|
||||
parentElement.data.insertChild(thisElement.data, hasActionBar ? index - 1 : index);
|
||||
})
|
||||
}
|
||||
`)
|
||||
|
||||
let dbg = x => {
|
||||
Js.log(x)
|
||||
x
|
||||
}
|
||||
let dbg2 = (x, lbl) => {
|
||||
Js.log2(lbl, x)
|
||||
x
|
||||
}
|
||||
|
||||
let flip: (('a, 'b) => 'c, 'b, 'a) => 'c = (fx, x, y) => fx(y, x)
|
||||
|
||||
/*
|
||||
|
||||
*/
|
@ -1,10 +1,31 @@
|
||||
module RootLayout = {
|
||||
%%private(
|
||||
@module("@nativescript/core") @new
|
||||
external new: unit => Types.nativeObject = "RootLayout"
|
||||
let layout = new()
|
||||
let attributes = Helper.getPropsForObject(Obj.magic(layout))
|
||||
layout.destroyNode(.)
|
||||
)
|
||||
|
||||
let tagName = "ns-root-layout"
|
||||
let make = class => class
|
||||
let handler: Types.handler = {
|
||||
init: (. ()) => new(),
|
||||
observedAttributes: attributes,
|
||||
render: Js.Nullable.return((. current: Types.this, _) =>
|
||||
if !Js.Nullable.isNullable(current.parentElement.data.insertChild) {
|
||||
Types.requestAnimationFrame(._ => Helper.addView(. current.parentElement, current))
|
||||
}
|
||||
),
|
||||
pageAdded: Js.Nullable.null,
|
||||
update: NativescriptCore.update,
|
||||
dispose: NativescriptCore.dispose,
|
||||
addEventListener: NativescriptCore.addEventListener,
|
||||
removeEventListener: NativescriptCore.removeEventListener,
|
||||
}
|
||||
}
|
||||
let all: array<Types.customElement> = [
|
||||
{
|
||||
tagName: RootLayout.tagName,
|
||||
make: RootLayout.make,
|
||||
handler: RootLayout.handler,
|
||||
},
|
||||
]
|
||||
|
1
nativescript-bind-res/Native/Layouts.resi
Normal file
1
nativescript-bind-res/Native/Layouts.resi
Normal file
@ -0,0 +1 @@
|
||||
let all: array<Types.customElement>
|
@ -1,2 +1,66 @@
|
||||
module Frame = {
|
||||
%%private(
|
||||
@module("@nativescript/core") @new
|
||||
external new: unit => Types.nativeObject = "Frame"
|
||||
let frame = new()
|
||||
let attributes = Helper.getPropsForObject(Obj.magic(frame))
|
||||
frame.destroyNode(.)
|
||||
)
|
||||
let tagName = "ns-frame"
|
||||
|
||||
let allElements: array<Types.customElement> = Belt.Array.concatMany([Elements.all, Layouts.all])
|
||||
let handler: Types.handler = {
|
||||
init: (. ()) => new(),
|
||||
observedAttributes: attributes,
|
||||
update: NativescriptCore.update,
|
||||
render: Js.Nullable.null,
|
||||
pageAdded: Js.Nullable.return((. current: Types.this) => {
|
||||
current.data->Types.navigate({
|
||||
create: _ =>
|
||||
(current.children->Array.unsafe_get(current.children->Array.length - 1)).data,
|
||||
})
|
||||
}),
|
||||
dispose: NativescriptCore.dispose,
|
||||
addEventListener: NativescriptCore.addEventListener,
|
||||
removeEventListener: NativescriptCore.removeEventListener,
|
||||
}
|
||||
}
|
||||
|
||||
module Page = {
|
||||
%%private(
|
||||
@module("@nativescript/core") @new
|
||||
external new: unit => Types.nativeObject = "Page"
|
||||
let page = new()
|
||||
let attributes = Helper.getPropsForObject(Obj.magic(page))
|
||||
page.destroyNode(.)
|
||||
)
|
||||
|
||||
let tagName = "ns-page"
|
||||
let handler: Types.handler = {
|
||||
init: (. ()) => new(),
|
||||
observedAttributes: attributes,
|
||||
update: NativescriptCore.update,
|
||||
render: Js.Nullable.return((. current: Types.this, nativeObject) => {
|
||||
// page should have one child which is a layout
|
||||
nativeObject->Types.setContent((current.children->Array.unsafe_get(0)).data)
|
||||
|
||||
Types.requestAnimationFrame(._ => {
|
||||
current.parentElement.handler.pageAdded
|
||||
->Js.Nullable.toOption
|
||||
->Belt.Option.forEach(fx => fx(. current.parentElement))
|
||||
})
|
||||
}),
|
||||
pageAdded: Js.Nullable.null,
|
||||
dispose: NativescriptCore.dispose,
|
||||
addEventListener: NativescriptCore.addEventListener,
|
||||
removeEventListener: NativescriptCore.removeEventListener,
|
||||
}
|
||||
}
|
||||
|
||||
let allElements: array<Types.customElement> = Belt.Array.concatMany([
|
||||
Elements.all,
|
||||
Layouts.all,
|
||||
[
|
||||
{tagName: Page.tagName, handler: Page.handler},
|
||||
{tagName: Frame.tagName, handler: Frame.handler},
|
||||
],
|
||||
])
|
||||
|
1
nativescript-bind-res/Native/Native.resi
Normal file
1
nativescript-bind-res/Native/Native.resi
Normal file
@ -0,0 +1 @@
|
||||
let allElements: array<Types.customElement>
|
@ -6,3 +6,15 @@ module Application = {
|
||||
@module("@nativescript/core") @scope("Application")
|
||||
external run: config => unit = "run"
|
||||
}
|
||||
|
||||
let update = (. nativeObject, attr, newValue) =>
|
||||
nativeObject->Obj.magic->Helper.update(attr, newValue)
|
||||
|
||||
let dispose = (. nativeObject: Types.nativeObject) => nativeObject.destroyNode(.)
|
||||
|
||||
let addEventListener = (. nativeObject: Types.nativeObject, event, callback) => {
|
||||
nativeObject.on(. event, callback)
|
||||
}
|
||||
let removeEventListener = (. nativeObject: Types.nativeObject, event, callback) => {
|
||||
nativeObject.off(. event, callback)
|
||||
}
|
||||
|
@ -1,4 +1,50 @@
|
||||
type event
|
||||
|
||||
type rec nativeObject = {
|
||||
on: (. event, event => unit) => unit,
|
||||
off: (. event, event => unit) => unit,
|
||||
destroyNode: (. unit) => unit,
|
||||
insertChild: Js.Nullable.t<(. nativeObject, int) => unit>,
|
||||
}
|
||||
|
||||
type constructor = {
|
||||
observedAttributes: array<string>,
|
||||
name: string,
|
||||
}
|
||||
|
||||
@val external requestAnimationFrame: (. unit => unit) => unit = "requestAnimationFrame"
|
||||
|
||||
@set
|
||||
external setContent: (nativeObject, 'a) => unit = "content"
|
||||
|
||||
type navigationConfig = {create: unit => nativeObject}
|
||||
|
||||
@send
|
||||
external navigate: (nativeObject, navigationConfig) => unit = "navigate"
|
||||
|
||||
type rec handler = {
|
||||
init: (. unit) => nativeObject,
|
||||
observedAttributes: array<string>,
|
||||
update: (. nativeObject, string, string) => unit,
|
||||
render: Js.Nullable.t<(. this, nativeObject) => unit>,
|
||||
pageAdded: Js.Nullable.t<(. this) => unit>,
|
||||
dispose: (. nativeObject) => unit,
|
||||
addEventListener: (. nativeObject, event, event => unit) => unit,
|
||||
removeEventListener: (. nativeObject, event, event => unit) => unit,
|
||||
}
|
||||
and this = {
|
||||
getAttribute: string => Js.Nullable.t<string>,
|
||||
style: string,
|
||||
constructor: constructor,
|
||||
parentElement: this,
|
||||
handler: handler,
|
||||
data: nativeObject,
|
||||
children: array<this>,
|
||||
navigate: this => unit,
|
||||
}
|
||||
@val external this: this = "this"
|
||||
|
||||
type customElement = {
|
||||
tagName: string,
|
||||
make: Obj.t => Obj.t,
|
||||
handler: handler,
|
||||
}
|
||||
|
37
nativescript-bind-res/custom.js
Normal file
37
nativescript-bind-res/custom.js
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
export const withCustomElements = (UIElement, handler) =>
|
||||
class extends UIElement {
|
||||
constructor() {
|
||||
super()
|
||||
this.handler = handler
|
||||
this.data = this.handler.init()
|
||||
console.log(`${this.tagName} created`)
|
||||
}
|
||||
static get observedAttributes() {
|
||||
return handler.observedAttributes
|
||||
}
|
||||
attributeChangedCallback(name, old, newValue) {
|
||||
this.handler.update(this.data, name, newValue)
|
||||
console.log(`${this.tagName} update`)
|
||||
|
||||
}
|
||||
connectedCallback() {
|
||||
if (this.isConnected && this.handler.render) {
|
||||
this.handler.render(this, this.data)
|
||||
}
|
||||
console.log(`${this.tagName} connected`)
|
||||
|
||||
}
|
||||
disconnectedCallback() {
|
||||
this.handler.dispose(this.data)
|
||||
console.log(`${this.tagName} disconnected`)
|
||||
}
|
||||
addEventListener(event, callback) {
|
||||
super.addEventListener(event, callback);
|
||||
this.handler.addEventListener(this.data, event, callback)
|
||||
}
|
||||
removeEventListener(event, callback) {
|
||||
super.removeEventListener(event, callback);
|
||||
this.handler.removeEventListener(this.data, event, callback);
|
||||
}
|
||||
}
|
2
nativescript-bind-res/index.d.ts
vendored
2
nativescript-bind-res/index.d.ts
vendored
@ -1,4 +1,4 @@
|
||||
declare module "App.bs" {
|
||||
declare module "App.bs.js" {
|
||||
type port = any
|
||||
type config = {
|
||||
elmModule: () => any,
|
||||
|
@ -10,7 +10,7 @@ external document: Dom.window => Dom.document = "document"
|
||||
@get
|
||||
external hTMLElement: Dom.window => Obj.t = "HTMLElement"
|
||||
|
||||
type customElement = {define: (string, Obj.t) => unit}
|
||||
type customElement = {define: (. string, Obj.t) => unit}
|
||||
|
||||
@get
|
||||
external customElements: Dom.window => customElement = "customElements"
|
||||
|
@ -1,24 +1,27 @@
|
||||
"use strict";
|
||||
const Element_1 = (require("happy-dom/lib/nodes/element/Element"));
|
||||
const HTMLUnknownElement_1 = (require("happy-dom/lib/nodes/html-unknown-element/HTMLUnknownElement"));
|
||||
const Text_1 = (require("happy-dom/lib/nodes/text/Text"));
|
||||
const Comment_1 = (require("happy-dom/lib/nodes/comment/Comment"));
|
||||
const Node_1 = (require("happy-dom/lib/nodes/node/Node"));
|
||||
const TreeWalker_1 = (require("happy-dom/lib/tree-walker/TreeWalker"));
|
||||
const DocumentFragment_1 = (require("happy-dom/lib/nodes/document-fragment/DocumentFragment"));
|
||||
const XMLParser_1 = (require("happy-dom/lib/xml-parser/XMLParser"));
|
||||
const Event_1 = (require("happy-dom/lib/event/Event"));
|
||||
const DOMImplementation_1 = (require("happy-dom/lib/dom-implementation/DOMImplementation"));
|
||||
const Attr_1 = (require("happy-dom/lib/attribute/Attr"));
|
||||
const NamespaceURI_1 = (require("happy-dom/lib/config/NamespaceURI"));
|
||||
const DocumentType_1 = (require("happy-dom/lib/nodes/document-type/DocumentType"));
|
||||
const ParentNodeUtility_1 = (require("happy-dom/lib/nodes/parent-node/ParentNodeUtility"));
|
||||
const QuerySelector_1 = (require("happy-dom/lib/query-selector/QuerySelector"));
|
||||
const DOMException_1 = (require("happy-dom/lib/exception/DOMException"));
|
||||
const HTMLCollectionFactory_1 = (require("happy-dom/lib/nodes/element/HTMLCollectionFactory"));
|
||||
const DocumentReadyStateEnum_1 = (require("happy-dom/lib/nodes/document/DocumentReadyStateEnum"));
|
||||
const DocumentReadyStateManager_1 = (require("happy-dom/lib/nodes/document/DocumentReadyStateManager"));
|
||||
const Selection_1 = (require("happy-dom/lib/selection/Selection"));
|
||||
const Element_1 = (require("../../node_modules/happy-dom/lib/nodes/element/Element"));
|
||||
const HTMLUnknownElement_1 = (require("../../node_modules/happy-dom/lib/nodes/html-unknown-element/HTMLUnknownElement"));
|
||||
const Text_1 = (require("../../node_modules/happy-dom/lib/nodes/text/Text"));
|
||||
const Comment_1 = (require("../../node_modules/happy-dom/lib/nodes/comment/Comment"));
|
||||
const Node_1 = (require("../../node_modules/happy-dom/lib/nodes/node/Node"));
|
||||
const TreeWalker_1 = (require("../../node_modules/happy-dom/lib/tree-walker/TreeWalker"));
|
||||
const DocumentFragment_1 = (require("../../node_modules/happy-dom/lib/nodes/document-fragment/DocumentFragment"));
|
||||
const XMLParser_1 = (require("../../node_modules/happy-dom/lib/xml-parser/XMLParser"));
|
||||
const Event_1 = (require("../../node_modules/happy-dom/lib/event/Event"));
|
||||
const DOMImplementation_1 = (require("../../node_modules/happy-dom/lib/dom-implementation/DOMImplementation"));
|
||||
|
||||
// const ElementTag_1 = (require("../../node_modules/happy-dom/lib/config/ElementTag"));
|
||||
|
||||
const Attr_1 = (require("../../node_modules/happy-dom/lib/attribute/Attr"));
|
||||
const NamespaceURI_1 = (require("../../node_modules/happy-dom/lib/config/NamespaceURI"));
|
||||
const DocumentType_1 = (require("../../node_modules/happy-dom/lib/nodes/document-type/DocumentType"));
|
||||
const ParentNodeUtility_1 = (require("../../node_modules/happy-dom/lib/nodes/parent-node/ParentNodeUtility"));
|
||||
const QuerySelector_1 = (require("../../node_modules/happy-dom/lib/query-selector/QuerySelector"));
|
||||
const DOMException_1 = (require("../../node_modules/happy-dom/lib/exception/DOMException"));
|
||||
const HTMLCollectionFactory_1 = (require("../../node_modules/happy-dom/lib/nodes/element/HTMLCollectionFactory"));
|
||||
const DocumentReadyStateEnum_1 = (require("../../node_modules/happy-dom/lib/nodes/document/DocumentReadyStateEnum"));
|
||||
const DocumentReadyStateManager_1 = (require("../../node_modules/happy-dom/lib/nodes/document/DocumentReadyStateManager"));
|
||||
const Selection_1 = (require("../../node_modules/happy-dom/lib/selection/Selection"));
|
||||
/**
|
||||
* Document.
|
||||
*/
|
||||
|
@ -1,18 +1,18 @@
|
||||
import Document from "./document";
|
||||
import Node_1 from "happy-dom/lib/nodes/node/Node";
|
||||
import CustomEvent_1 from "happy-dom/lib/event/events/CustomEvent";
|
||||
import HTMLElement_1 from "happy-dom/lib/nodes/html-element/HTMLElement";
|
||||
import CustomElementRegistry_1 from "happy-dom/lib/custom-element/CustomElementRegistry";
|
||||
import EventTarget_1 from "happy-dom/lib/event/EventTarget";
|
||||
const Node_1 = require("../../node_modules/happy-dom/lib/nodes/node/Node");
|
||||
const CustomEvent_1 = require("../../node_modules/happy-dom/lib/event/events/CustomEvent");
|
||||
const HTMLElement_1 = require("../../node_modules/happy-dom/lib/nodes/html-element/HTMLElement");
|
||||
const CustomElementRegistry_1 = require("../../node_modules/happy-dom/lib/custom-element/CustomElementRegistry");
|
||||
const EventTarget_1 = require("../../node_modules/happy-dom/lib/event/EventTarget");
|
||||
|
||||
export class Window extends EventTarget_1 {
|
||||
export class Window extends EventTarget_1.default {
|
||||
constructor() {
|
||||
super();
|
||||
this.Node = Node_1;
|
||||
this.CustomEvent = CustomEvent_1;
|
||||
this.HTMLElement = HTMLElement_1;
|
||||
this.customElements = new CustomElementRegistry_1();
|
||||
const document = new Document(this)
|
||||
this.Node = Node_1.default;
|
||||
this.CustomEvent = CustomEvent_1.default;
|
||||
this.HTMLElement = HTMLElement_1.default;
|
||||
this.customElements = new CustomElementRegistry_1.default();
|
||||
const document = new Document(this)
|
||||
this.document = document;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "elm-native",
|
||||
"main": "src/index.ts",
|
||||
"main": "src/index.js",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@ -14,10 +14,11 @@
|
||||
"@nativescript/core": "~8.4.0",
|
||||
"@nativescript/theme": "~3.0.2",
|
||||
"elm": "^0.19.1-5",
|
||||
"happy-dom": "^8.1.2",
|
||||
"happy-dom": "^6.0.4",
|
||||
"vm-shim": "^0.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nativescript/ios": "8.3.3",
|
||||
"@nativescript/types": "~8.4.0",
|
||||
"@nativescript/webpack": "~5.0.12",
|
||||
"elm-webpack-loader": "^8.0.0",
|
||||
|
95
src/Main.elm
95
src/Main.elm
@ -1,46 +1,93 @@
|
||||
module Main exposing (main)
|
||||
|
||||
import Browser
|
||||
import Html exposing (Html, text)
|
||||
import Html exposing (Html)
|
||||
import Json.Decode as D
|
||||
import Native
|
||||
import Native.Attributes
|
||||
import Native.Event as Event
|
||||
import Native.Frame as Frame
|
||||
import Native.Layout exposing (rootLayout)
|
||||
import Native.Page as Page
|
||||
|
||||
|
||||
main : Program () Model Msg
|
||||
main =
|
||||
Browser.sandbox
|
||||
{ init = init
|
||||
, view = view
|
||||
, update = update
|
||||
}
|
||||
|
||||
|
||||
type NavPage
|
||||
= Details
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ count : Int }
|
||||
{ count : Int
|
||||
, next : Maybe NavPage
|
||||
, history : List NavPage
|
||||
}
|
||||
|
||||
|
||||
initialModel : Model
|
||||
initialModel =
|
||||
{ count = 0 }
|
||||
init : Model
|
||||
init =
|
||||
let
|
||||
_ =
|
||||
Debug.log "HEllo from ELM" "World"
|
||||
in
|
||||
{ count = 0, history = [ Details ], next = Nothing }
|
||||
|
||||
|
||||
type Msg
|
||||
= Increment
|
||||
| Decrement
|
||||
= Inc
|
||||
| Dec
|
||||
| GoToDetails
|
||||
| Destory
|
||||
|
||||
|
||||
update : Msg -> Model -> Model
|
||||
update msg model =
|
||||
case msg of
|
||||
Increment ->
|
||||
Dec ->
|
||||
{ model | count = model.count - 1 }
|
||||
|
||||
Inc ->
|
||||
{ model | count = model.count + 1 }
|
||||
|
||||
Decrement ->
|
||||
{ model | count = model.count - 1 }
|
||||
GoToDetails ->
|
||||
{ model | history = Details :: model.history, next = Just Details }
|
||||
|
||||
Destory ->
|
||||
{ model
|
||||
| next = Nothing
|
||||
, history =
|
||||
case model.next of
|
||||
Nothing ->
|
||||
model.history |> List.drop 1
|
||||
|
||||
Just _ ->
|
||||
model.history
|
||||
}
|
||||
|
||||
|
||||
detailsPage : Model -> Page.Page Msg
|
||||
detailsPage _ =
|
||||
Page.page [
|
||||
-- Event.on "navigatedTo" (D.succeed Destory)
|
||||
]
|
||||
(rootLayout []
|
||||
[ Native.label [ Native.Attributes.text "Hello from elm" ] []
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
view : Model -> Html Msg
|
||||
view model =
|
||||
text "Hello"
|
||||
|
||||
|
||||
main : Program () Model Msg
|
||||
main =
|
||||
let
|
||||
_ =
|
||||
Debug.log "INIT" "ELM"
|
||||
in
|
||||
Browser.sandbox
|
||||
{ init = initialModel
|
||||
, view = view
|
||||
, update = update
|
||||
}
|
||||
Frame.frame
|
||||
model
|
||||
[ ( Details, detailsPage )
|
||||
]
|
||||
[]
|
||||
|> Frame.root
|
||||
|
@ -1,4 +1,5 @@
|
||||
const webpack = require("@nativescript/webpack");
|
||||
const path = require('path');
|
||||
|
||||
module.exports = (env) => {
|
||||
webpack.init(env);
|
||||
|
Loading…
Reference in New Issue
Block a user