├── .gitignore
├── elm-package.json
├── README.md
├── LICENSE
├── index.html
├── elm-custom-element.js
├── src
└── Main.elm
└── CODE_OF_CONDUCT.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /elm-stuff/
2 | /build/
3 |
--------------------------------------------------------------------------------
/elm-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "summary": "helpful summary of your project, less than 80 characters",
4 | "repository": "https://github.com/user/project.git",
5 | "license": "BSD3",
6 | "source-directories": [
7 | "./src"
8 | ],
9 | "exposed-modules": [],
10 | "dependencies": {
11 | "elm-lang/core": "5.0.0 <= v < 6.0.0",
12 | "elm-lang/html": "2.0.0 <= v < 3.0.0"
13 | },
14 | "elm-version": "0.18.0 <= v < 0.19.0"
15 | }
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Elm Custom Element Demo
2 |
3 | http://lukewestby.com/elm-custom-element-demo
4 |
5 | Demonstrates an Elm application running inside of a W3C Custom Element. For
6 | best results, use with a browser known to support the Custom Elements API. I
7 | tested this in Chrome 49.
8 |
9 | ## How it works
10 |
11 | The Elm application defines a `main` exposing a `Signal Html`, like a typical
12 | start-app application. It also exposes a port named `attributes` which allows in
13 | a `Json.Decode.Value` representing the current value of the custom element's
14 | attributes. Lastly, it exposes a port named `events` which maps values onto
15 | event names. Every time a new value is detected on one of the record fields in
16 | `events`, a custom DOM event is triggered with the name of the field and the
17 | associated value is placed on `event.detail`.
18 |
19 | Check out `src/Main.elm` and `index.html` to see related app code, and see
20 | `elm-custom-element.js` for the wire-up Custom Element code that comprises
21 | `ElmCustomElement.register()`.
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Luke Westby
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Elm Custom Element Demo
6 |
7 |
8 |
9 | Open Developer Tools!
10 | Inspect this element! ↓
11 |
12 |
13 |
14 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/elm-custom-element.js:
--------------------------------------------------------------------------------
1 | window.ElmCustomElement = (function () {
2 |
3 | function attributesToObject(attributes) {
4 | return Array.prototype.slice.call(attributes)
5 | .reduce(function (current, next) {
6 | current[next.name] = next.value;
7 | return current;
8 | }, {});
9 | }
10 |
11 | function register(tagName, elmModule) {
12 | var ElementProto = Object.create(HTMLElement.prototype);
13 |
14 | ElementProto.createdCallback = function createdCallback() {
15 | var self = this;
16 | self._attributesJson = attributesToObject(this.attributes);
17 | self._wrapper = document.createElement('div');
18 | self._elmApp = elmModule.embed(this._wrapper, { attributes: this._attributesJson });
19 | self._shadow = this.createShadowRoot();
20 | self._shadow.appendChild(this._wrapper);
21 | self._previousEventValues = {};
22 | self._elmApp.ports.events.subscribe(function (nextValues) {
23 | Object.keys(nextValues).forEach(function (key) {
24 | var nextValue = nextValues[key];
25 | if (self._previousEventValues[key] === nextValue) return;
26 | self._previousEventValues[key] = nextValue;
27 | var event = new CustomEvent(key, { detail: nextValue });
28 | self.dispatchEvent(event);
29 | });
30 | });
31 | };
32 |
33 | ElementProto.attachedCallback = function attachedCallback() {
34 | var self = this;
35 | self._attributesJson = attributesToObject(self.attributes);
36 | self._elmApp.ports.attributes.send(self._attributesJson);
37 | }
38 |
39 | ElementProto.attributeChangedCallback = function attributeChangedCallback() {
40 | var self = this;
41 | self._attributesJson = attributesToObject(self.attributes);
42 | self._elmApp.ports.attributes.send(self._attributesJson);
43 | };
44 |
45 | document.registerElement(tagName, {
46 | prototype: ElementProto,
47 | });
48 | }
49 |
50 | return {
51 | register,
52 | };
53 | }());
54 |
--------------------------------------------------------------------------------
/src/Main.elm:
--------------------------------------------------------------------------------
1 | port module Main exposing (..)
2 |
3 | import Html as H exposing (Html)
4 | import Html.Attributes as HA
5 | import Json.Decode as Json exposing (Decoder)
6 | import Process
7 | import Task
8 |
9 |
10 | main : Program Json.Value Model Msg
11 | main =
12 | H.programWithFlags
13 | { init = initialState
14 | , update = update
15 | , view = view
16 | , subscriptions = \_ -> Sub.batch [ attributes AttributesChange ]
17 | }
18 |
19 |
20 | type alias Model =
21 | { count : Int
22 | , color : String
23 | }
24 |
25 |
26 | initialState : Json.Value -> ( Model, Cmd Msg )
27 | initialState attrs =
28 | ( { count = 0
29 | , color =
30 | attrs
31 | |> Json.decodeValue attributeDecoder
32 | |> Result.withDefault defaultAttributes
33 | |> .color
34 | }
35 | , waitAndUpdate
36 | )
37 |
38 |
39 | type alias Attributes =
40 | { color : String }
41 |
42 |
43 | defaultAttributes : Attributes
44 | defaultAttributes =
45 | { color = "#0F0" }
46 |
47 |
48 | type Msg
49 | = UpdateCounter
50 | | AttributesChange Attributes
51 |
52 |
53 | update : Msg -> Model -> ( Model, Cmd Msg )
54 | update msg model =
55 | case msg of
56 | UpdateCounter ->
57 | let
58 | newCount =
59 | model.count + 1
60 | in
61 | { model | count = model.count + 1 }
62 | ! [ waitAndUpdate
63 | , events <| Events <| newCount
64 | ]
65 |
66 | AttributesChange attributes ->
67 | { model | color = attributes.color } ! []
68 |
69 |
70 | waitAndUpdate : Cmd Msg
71 | waitAndUpdate =
72 | Process.sleep 1000
73 | |> Task.perform (always UpdateCounter)
74 |
75 |
76 | type alias Events =
77 | { change : Int
78 | }
79 |
80 |
81 | port events : Events -> Cmd msg
82 |
83 |
84 | port attributes : (Attributes -> msg) -> Sub msg
85 |
86 |
87 | view : Model -> Html Msg
88 | view model =
89 | H.div [ HA.style [ ( "color", model.color ) ] ]
90 | [ H.text <| toString model ]
91 |
92 |
93 | attributeDecoder : Decoder Attributes
94 | attributeDecoder =
95 | Json.string
96 | |> Json.at [ "color" ]
97 | |> Json.map Attributes
98 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at opensource@lukewestby.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------