Coder Social home page Coder Social logo

modular-ui-roadmap's People

Contributors

danielnarey avatar

Stargazers

 avatar

Watchers

 avatar  avatar

modular-ui-roadmap's Issues

Roadmap: A utility, not a framework

Some thoughts I needed to get down before proceeding (this is long):

My personal approach to web development has been evolving rapidly since I published the Semantic DOM and Modular UI packages. At that time, I thought that web development could be simplified, or at least rationalized, by having one framework to rule them all. Now I think that being able to flexibly integrate different sets of utilities that are each designed to solve a specific problem is probably a better approach in the long run, but more on that later.

The advantage I saw in Elm as a front end framework is that it provides a common functional syntax and set of language utilities for markup, event handling, and application logic. Some other great things about Elm: immutability of data/objects, no global scope, no side effects allowed. (As a side note: A lot of Elm folks are big on type specificity—I get the point in terms of catching errors at compile time, but it also results in code duplication and more cumbersome error handling, so when we have a choice about how to implement something, it's important to ask how useful type checking is in that instance).

I also saw some shortcomings with Elm as a front end framework. First, once you get beyond "Hello, World," writing markup with the Html package is no fun (for me anyway). Multiple nested square brackets don't make for very readable or maintainable code, and the pipe operator won't help because VirtualDom nodes are unmodifiable once they are constructed. Second, for all that Elm does to simplify event handling, application logic, and state management, Elm has no utilities out of the box that would help to simplify the user interface side of front end development. Hype and corporate sponsorship aside, one of the selling points for Redux/React as a development framework is that it has React, "A javascript library for building user interfaces," whereas Elm has...the Html package?

Modular UI was an attempt to get around the first shortcoming (frustrating syntax/constrained patterns) and to try out some strategies for dealing with the second (lack of a built-in component architecture). In the syntax/pattern aspect, I think that my approach in Modular UI has been mostly successful because it aligns with a functional way of breaking down chunks of code, and with functional programming patterns more generally. In terms of moving toward a component architecture, my thinking has shifted since I last worked on this package, and I'd like to explain how.

There are two ways I tried to think about component architecture in Modular UI. The first was to build event handling into constructors for basic components like buttons and input fields—e.g., pass a message type and other data to a constructor function and get a responsive component that's hooked up to your app's update function. I find it to be really useful, even for something as simple as a button, not to have to think about how to write the markup every time, but just to be able to invoke a function and get the component I need. So far, so good.

The second line of thinking regarding component architecture is how to go beyond basic things like buttons and input fields in order to build to more complex components that could be reused (with variation) in lots of different projects. This is where I got stuck for a couple of reasons. Because I was thinking in a "framework" paradigm, I turned to popular CSS frameworks to figure out what components I should include and how to pattern them. I've had some frustrations with Bootstrap in the past; in discovering Bulma CSS as a newer alternative, I liked its class-only approach to styling, so I chose to integrate Bulma class names and to pattern components using the corresponding markup in the Bulma docs. In the Modular UI package, I only included Bulma's basic element classes, but I also have unreleased code with constructor functions for more complex Bulma components (here's one for tabs from the docs page).

Tying Modular UI to a specific CSS framework is an obvious limitation to its general usefulness and adoptability. At the same time, it's useful to have reusable components that can be plugged in to any view.

So here's my agenda:

1. Separate out the two tasks of (a) stabilizing a functional syntax for UI development in Elm and (b) moving toward a general component architecture that can guide the design of specific component libraries.

  1. The next version of Modular UI for Elm 0.19 should only address task (a). That will make it much smaller and easier to learn, and it will remove any reference to or dependency on Bulma CSS. The necessary parts of Semantic DOM should also be made into a internal/unexposed modules for Modular UI, rather than a separate package, so that it is entirely self-contained with no dependencies except Elm VirtualDom.

  2. The idea that I have in mind for task (b) is to create utilities that make it easier to develop components as needed, rather than creating the components themselves. I got this idea from Tailwind CSS, which is a PostCSS plugin that enables you to easily assemble custom CSS components by combining selectors with low-level utility classes. I'm not sure exactly how this translates, but it makes sense to me conceptually.

Simplified API (open to discussion)

Requesting input from @arsduo, @carolineartz, and other users:

Background and motivation

Read here if you are interested.

Semantics of record updates

When argument is a single value or key/value pair:

  • add__ = append a value or key/value pair to the current list contained in a field
  • add__Conditional = same as add__, but only updates when a conditional expression evaluates to True
  • remove__ = delete all instances of a value or key from the current list contained in a field
  • replace__ = delete the current value contained in a field, replacing it with the new value

When argument is a list:

  • add__List = append a list to the current list contained in a field
  • add__ListConditional = same as add__List, but only updates when a conditional expression evaluates to True
  • replace__List = delete the current list contained in a field, replacing it with the new list (to remove the current list, just replace it with [])

Variations for particular fields:

  • set__ = same as replace__, but signals that field that should only need to be updated once
  • append__ and prepend__ = used instead of add__ to provide an option for inserting the argument before or after the field's current string/list

Core module

Discussion of name is in issue #4

Types:

type Element msg =
  Element (Data msg)

type alias Data msg =
  { tag : String
  , id : String
  , classes : List String
  , styles : List (String, String)
  , attributes : List (VirtualDom.Attribute msg)
  , listeners: List (String, VirtualDom.Handler msg)
  , text : String
  , children : List (VirtualDom.Node msg)
  , namespace : String
  , keys : List String
}

Constructor for element records:
This replaces leaf, textWrapper, and container with a single constructor. The string argument sets the tag. See below for how to add child nodes.

element : String -> Element msg

Modifier functions for attributes:
In the previous version, "modifier" had a specific meaning pertaining to style classes that modify a base class. In the new version, I want to use "modifier" to mean any function that takes an existing element record, updates some of its internal data, and returns the updated element record. I'd like to call these "modifier functions" instead of "update functions" to make clear that they are just part of the view code and don't have anything to do with the Elm program's update function.

-- ID --

setId : String -> Element msg -> Element msg

-- CLASS --

addClass : String -> Element msg -> Element msg
addClassConditional : String -> Bool -> Element msg -> Element msg
addClassList : List String -> Element msg -> Element msg
addClassListConditional : List String -> Bool -> Element msg -> Element msg
removeClass : String -> Element msg -> Element msg
replaceClassList : List String -> Element msg -> Element msg

-- STYLE --

addStyle : (String, String) -> Element msg -> Element msg
addStyleConditional : (String, String) -> Bool -> Element msg -> Element msg
addStyleList : List (String, String) -> Element msg -> Element msg
addStyleListConditional : List (String, String) -> Bool -> Element msg -> Element msg
removeStyle : String -> Element msg -> Element msg
replaceStyleList : List (String, String) -> Element msg -> Element msg

-- OTHER ATTRIBUTES --
-- you can add any attribute using an `Html.Attribute` function or `VirtualDom` primitive

addAttribute : VirtualDom.Attribute msg -> Element msg -> Element msg
addAttributeConditional : VirtualDom.Attribute msg -> Bool -> Element msg -> Element msg
addAttributeList : List (VirtualDom.Attribute msg) -> Element msg -> Element msg
addAttributeListConditional : List (VirtualDom.Attribute msg) -> Bool -> Element msg -> Element msg
replaceAttributeList : List (VirtualDom.Attribute msg) -> Element msg -> Element msg

-- EVENT LISTENERS --

---- ACTIONS 
---- send a message in response to a mouse or keyboard event

addAction : (String, msg) -> Element msg -> Element msg
addActionConditional : (String, msg) -> Bool -> Element msg -> Element msg
addActionStopPropagation : (String, msg) -> Element msg -> Element msg
addActionPreventDefault : (String, msg) -> Element msg -> Element msg
addActionStopAndPrevent : (String, msg) -> Element msg -> Element msg

---- INPUT HANDLERS
---- capture a value from the event target on "input" or "change"

addInputHandler : (String -> msg) -> Element msg -> Element msg
addInputHandlerWithParser : (a -> msg, String -> a) -> Element msg -> Element msg
addChangeHandler : (String -> msg) -> Element msg -> Element msg
addChangeHandlerWithParser : (a -> msg, String -> a) -> Element msg -> Element msg
addToggleHandler : (Bool -> msg) -> Element msg -> Element msg

---- CUSTOM LISTENERS
---- use a custom decoder to construct a listener for any event type 

addListener : (String, Decoder msg) -> Element msg -> Element msg
addListenerConditional : (String, Decoder msg) -> Bool -> Element msg -> Element msg
addListenerStopPropagation : (String, Decoder msg) -> Element msg -> Element msg
addListenerPreventDefault : (String, Decoder msg) -> Element msg -> Element msg
addListenerStopAndPrevent : (String, Decoder msg) -> Element msg -> Element msg

---- REMOVE A LISTENER
---- works for actions, input handlers (with "input" or "change"), and custom listeners

removeListener: String -> Element msg -> Element msg

Modifier functions for internal text:
Text is rendered to VirtualDom as the first child node, containing plain HTML text. Helper functions for styling text that were included in the previous version will be moved to a separate package.

appendText : String -> Element msg -> Element msg
appendTextConditional : String -> Bool -> Element msg -> Element msg
prependText : String -> Element msg -> Element msg
prependTextConditional :  String -> Bool -> Element msg -> Element msg
replaceText : String -> Element msg -> Element msg
replaceTextConditional :  String -> Bool -> Element msg -> Element msg

Modifier functions to construct an element's internal tree:
Child elements are immediately rendered to VirtualDom when this function is executed. setChildListWithKeys uses the VirtualDom.KeyedNode optimization and should only be called once on any given node.

appendChild : Element msg -> Element msg -> Element msg
appendChildConditional : Element msg -> Bool -> Element msg -> Element msg
appendChildList : List (Element msg) -> Element msg -> Element msg
appendChildListConditional : List (Element msg) -> Bool -> Element msg -> Element msg
appendNodeList : List (VirtualDom.Node msg) -> Element msg -> Element msg
prependChild : Element msg -> Element msg -> Element msg
prependChildConditional : Element msg -> Bool -> Element msg -> Element msg
prependChildList : List (Element msg) -> Element msg -> Element msg
prependChildListConditional : List (Element msg) -> Bool -> Element msg -> Element msg
prependNodeList : List (VirtualDom.Node msg) -> Element msg -> Element msg
replaceChildList : List (Element msg) -> Element msg -> Element msg
replaceNodeList : List (VirtualDom.Node msg) -> Element msg -> Element msg
setChildListWithKeys : List (String, Element msg) -> Element msg -> Element msg
setNodeListWithKeys : List (String, VirtualDom.Node msg) -> Element msg -> Element msg

Modifier functions to set tag and namespace
The tag is already set with the element constructor function, but it could be useful to be able to change it when using component libraries. Namespace would typically be used to construct SVG nodes. Whenever the namespace field is not an empty string, VirtualDom.nodeNS is used for rendering.

setTag : String -> Element msg -> Element msg
setNamespace : String -> Element msg -> Element msg

Rendering
This function only needs to be called on the root node of a tree. VirtualDom.Node is interchangeable with Html.Html.

render : Element msg -> VirtualDom.Node msg

Getting up to speed with Elm 0.19

Well, it's a good thing that I was behind schedule in getting back to work on Modular UI, because Elm 0.19 released this week without much warning:

https://github.com/elm/compiler/blob/master/upgrade-docs/0.19.md
https://elm-lang.org/blog/small-assets-without-the-headache

There are breaking changes in repo location, core modules, and package configuration that will affect all community packages. Because the VirtualDom and Html packages appear mostly unchanged, I expect this upgrade to have minimal impact on design considerations for Modular UI, but it will still take some extra work to figure out what is new with 0.19 and what will have to be modified from 0.18 dependencies.

Package / core module name

I've been debating what to call the new package since it's going to replace elm-semantic-dom and also include the core parts of elm-modular-ui.

When I think of UI, I think of components, so if the package is going to be limited to low-level functions, Ui.__ doesn't seem right to me. Since Dom is no longer used as a core module name in the official Elm packages, I'm leaning toward elm-dom as the package name with Dom.elm as the module. Then you get Dom.Element, Dom.addClass, Dom.addChild, etc. I think it's also important at this point to adopt the convention that the package name and the core module name should match.

Any reservations about this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.