Giter VIP home page Giter VIP logo

doriansmiley / lotusjs Goto Github PK

View Code? Open in Web Editor NEW
11.0 11.0 3.0 22.44 MB

LotusJS is a framework for developing HTML5 applications using web components and TypeScript. It uses a functional style and leverages ramda for composition and currying. Lotus is opinionated about two things: A functional style and separating presentation for code.

License: Other

JavaScript 22.39% Shell 0.60% HTML 28.58% TypeScript 39.09% CSS 2.71% Dockerfile 1.51% SCSS 5.12%

lotusjs's People

Contributors

dependabot[bot] avatar doriansmiley avatar dylanjha avatar gitter-badger avatar renovate-bot avatar ryanlacdao avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

lotusjs's Issues

Create an AMP demo

Try doing a demo page and see if we can use AMP documents in component skins.

About:
https://www.ampproject.org/learn/about-how/

Amp also has it's own custom elements core:
https://www.ampproject.org/docs/reference/components

From what I am reading you would not include your own custom elements inside an AMP document. Instead, have your components load AMP documents as part of their skin. This thread has some info: ampproject/amphtml#5216. This makes the bulk of the content an AMP document I think as the skin is just a static HTML template.

Create first AMP page:
https://www.ampproject.org/docs/tutorials/create

Creating responsive pages:
https://www.ampproject.org/docs/guides/responsive/responsive_design

Create controller map

Control Mapping

Lotus.Context.controllerMap.mapView({component:'SomeFunction', mediator:SpiSdk.SomeMediator });
Lotus.Context.controllerMap.unmapView({component:'SomeFunction', mediator:SpiSdk.SomeMediator });

After view components have been processed by x-tag the context iterates over the view component instances stored in the Lotus.ComponentMap.componentInstances and if the component instance constructor matches a function mapped to the controller map it creates new controller instance, stores a reference to it, and calls the controller's onRegister method passing the view component instance. Be sure the controller instance is typed into the component lifecycle similar to view components. Event dispatching seems like the obvious choice on how to do this.

Controllers
required, all controllers must extend AbstractController

Lotus.AbstractController = function(){
//add view getter (read only!)
}

Lotus.AbstractController.prototype.onRegister = function( element ){
//stub for override
}

Lotus.AbstractController.prototype.addEventListeners = function( element ){
//stub for override, attach event listeners on the view
//mediators are expected to have some knowledge of the view they are mediating, IE what events to listen for on what part of the view
}

Lotus.AbstractController.prototype.removeEventListeners = function( element ){
//stub for override
}

//called by mediator map when element is removed
Lotus.AbstractController.prototype.onRemove = function( event ){
this.destroy();
}

Lotus.AbstractController.prototype.destory = function(){
//stub for override
}

Create Lotus CLI for starting projects

#Refer to the following article for building a CLI in NodeJS:
https://developer.atlassian.com/blog/2015/11/scripting-with-node/
https://www.sitepoint.com/javascript-command-line-interface-cli-node-js/

An archive with the required template files is here: https://www.dropbox.com/s/dkld3z00fy1hpb0/lotusEmptyProjectFiles.zip?dl=0

The tool should take in a project name and a namespace. Once executed the tool will:

  • Add webpack.config.js setting
entry: {
        'myNamespace-UMD': './src/index.ts',
        'myNamespace-UMD.min': './src/index.ts'
    }
....
output: {
        ....
        library: 'myNamespace',
       ....
    }
externals: {
        "lavenderjs/lib": "Lavender",
        "lotusjs-components/lib": "Lotus"
    }

where myNamespace was is the one supplied by the user.

  • Add tsconfig.json
  • Add package.json
  • Add .gitignore
  • Add .npmignore
  • Create globals/Globals.js setting up namespace with the one provided by the user
  • Create empty js directory
  • Create empty css directory
  • Create empty index.html file
  • Create empty lib directory
  • Create scripts directory with the following files: commands.txt, testSauce.sh, typescript.sh. Commands.txt will be writtes as follows:
#test on sauce labs
export HOME=PATH

cd $HOME
sh ./scripts/testSauce.sh

#test local
export HOME=PATH

cd $HOME
npm test

#compile typescript and webpack
export HOME=PATH
cd $HOME
npm run build

Where HOME is the path to the current working directory

  • Create a src directory with the following files: context/Context.ts and index.ts. Context.ts will be written as follows:
import * as Lavender from 'lavenderjs/lib';
import * as Lotus from 'lotusjs-components/lib';

export class Context extends Lotus.Context{
    
}

index.ts will be written as follows:

export * from './context/Context';
  • Create a test directory with the following files: config/typescript-test-karma.conf.js, config/karma.conf-ci.js, and an empty unit directory. Both files need the following path adjusted:
'../lib/myNamespace-UMD.min.js',

Where myNamespace is the namepace supplied by the user.

  • run npm install with --save-dev passed if the dev flag was entered by the user

x-tag v2

would you mind a pull request for a build involving a v2 custom elements LotusJS?

The version of x-tags v2 is off branch though, but I'm looking for feedback and it would be very helpful for v2.

Look into an analyzer for webcomponents.org

I'd like to be able to publish lotus components to webcomponents.org and get the same level of analysis of the component API as Polymer. The publication guide is here: https://www.webcomponents.org/publish . It states:

How do I add API documentation?

Currently, each repository is processed using Polymer/polymer-analyzer. Want to add an analyzer for your library? Chat to us on Gitter.

The documentation for your element is drawn from the README.md file on its GitHub repository. To make sure people can easily use your element, it's good practice to include:

A brief summary of what your element does
System requirements for end-users displaying your element in a web browser
Attributes of your element and possible values
A code sample showing how to include and use your element in an HTML page
For Polymer elements, see "Document your elements" for more information.

The Polymer analyzer is here: https://github.com/Polymer/polymer-analyzer

Last few example app fix ups

  • remove reference to context in abstract component
  • add label component to demonstrate data binding
  • update read.me with data binding example
  • move image gallery and item to core
  • Add brief explanation after the first paragraph in Web Components explaining how to create a custom component that extends abstract component. Also explain the attribute- attribute.

.gitignore node_modules?

Suggest including node_module dependencies. (ie: lavenderjs and x-tag) so that people can run the examples without npm installing.

Migrate actions from lavender to lotus

Remove the abstract actions from Lavender. They really belong in lotus. Also move over the ActionErrorEvent and ActionSuccessEvent. Also move over ModelLocator, Config, ErrorModel, AsyncOperationModel and RecordSetModel.

Optimize tree shaking for typescript ES modules (transitive dependencies)

npm will install specific module dependencies for each module consumed in our project. that means if we want to install component A that uses [email protected] and component B that uses [email protected] the resulting module will get both version leading to code bloat. We need a way to optimize the imports so that we don't get too much redundant code.

Description of how npm dependencies work is here: https://docs.npmjs.com/how-npm-works/npm3-nondet

Description of requireJS is here: https://bytearcher.com/articles/loading_modules_with_require/

"Npm takes care of installing transitive dependencies. For each module it checks if it depends on any module that is not already present in parent level node_modules directory. For any module having missing transitive dependencies, it acts if calling npm install inside that module's directory with one exception.

The exception is that it only installs dependencies not present in any parent node_modules directories. It only installs necessary missing dependencies."

I'm not seeing this though. For example in the spi S1 ref app the lotus-mvw node module directory contains a lotusjs-components module. The package.json versions look identical so we'll have to test this.

Information on Yarn, a new package manager used by Polymere is here: https://www.npmjs.com/package/yarn . It is deterministic and built for client side optimization.

Note this is only an issue for typescript modules. It does not effect the UMD modules because webpack excludes the Lotus and Lavender libs as follows:

externals: {
        "lavenderjs/lib": "Lavender",
        "lotusjs-components/lib": "Lotus"
    },

When the module is loaded in a web page or the global namespace is already defined. However typescript ES modules include require statements, and in it's in the4se cases we coule have an issue. My hope is this problem is solved by require by importing from the top level first.

Implement suggestions from Steve

  • Create docs section for LotusJS.org using type doc
  • Implement Pretty Print for code blocks
  • Enable Wiki in GitHub repo. Now public
  • Set up chatroom

Create example application

The app should effectively demonstrate the following:
Creation of a custom context
dependency injection
component mapping
creation of custom component
command mapping
creation of custom action + commands + event
Custom service implementation (remember these are registered with the injector!)
Custom parsing implementation (remember these are registered with the injector!)
Creation of a custom model
Data binding
Use of the event bus (commands will do that)
Use of object level event dispatching
Binding the view to the model and the model to the view using the component controller (two view objects updating the same model object should take care of this)

An RSS reader would be cool because the API is public, or a weather app as there are some public weather APIs as well.

Engineer Polymer support

This will require a component map factory which we will add to the Context by default. That way users can either use the x-tag based component map or the Polymer component map based on their application configuration.

Explore how to cretaed nested applications

IMPORTANT: this is not a priority item at this time, hence no milestone.

The context is effectively sand boxed so applications can in theory live side by side. However applications can not be nested at this time. This has more to do with xTag and it's parsing of the DOM than anything else. If you had applications that were nested inside each other there could be a case where the same component instances are mapped to two different contexts. You need some mechanism to tell xTag where to start parsing and where when to stop traversing children of the top level tag. These would effectively serve as application boundaries. It's important to be able to nest applications so they can be built as modules and loaded at runtime.

Note: if you avoid tag name collisions then apps should be able to live side by side.

Figure out why inherited accessor methods are not enumerated in subclasses

Below is the AbstractComponent class method that is effected by this. See notes in the property checks

public addAttributes():void{
for( let i=0; i < this.element.attributes.length; i++ ){
let attribute = this.element.attributes[i];
if( attribute.name.indexOf('attribute') >= 0 ){
let index = attribute.name.indexOf('attribute') + 10;
let newProp = attribute.name.substring(index);//remove prefix
//convert dashes to camel case
//LEGACY: using the data- prefix should trigger camel case on dash automagically
let camelCased = newProp.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
//typescript for some reason, even though the compiler is using Object.defineProperty passing the prototype with enumerable set to true, is emitting code that hides accesssor methods in sub classes
//this requires we check the prototype and '' + camelCased to find them.
//this required private properties use the _ in their name though which is not ideal
//TODO: figure out why accessor methods in base classes are not showing up on the enumerated properties of sub classes
if( this.hasOwnProperty(camelCased) || Object.getPrototypeOf(this).hasOwnProperty(camelCased) || this.hasOwnProperty('
' + camelCased)){
this[camelCased] = attribute.value;
}
}
}
}

Get unit tests passing

Getting several errors with Cannot read property 'injector' of undefined in the sample app

api reference glossary

Start an API reference library.
Suggestion: Start it on the gh-repo wiki for contributors for now.
Reason: For quick referencing of parts of the API

Create typescript injector and data binding utils using decorators

You can use decorators to do DI, scopes, etc. Info on how to build decorators is here: http://blog.wolksoftware.com/decorators-metadata-reflection-in-typescript-from-novice-to-expert-part-4

Better yet there is an IOC framework for typekit. See if you can extend it to provide the decorators for DI in typescript and hook up to function overrides that will work with Lotus injector and scopes. Framework is here: https://www.npmjs.com/package/typescript-ioc

I'd like to include an @bindable decorator that will mixin subject if the object does not extend it, and add the notify call to the setter. See if we could also use @bindable on a public property replacing the public property with a private one, and adding the accessor methods.

Explore custom xTag build

IMPORTANT: This is not a priority at this time.

See if there is a way to create a custom xTag build that only incorporates lifecycle functions as that is all we need. This could reduce the size of the required xTag lib.

Create singleton component

We should also expose a method to map a singleton component controller. We need an abstract base singleton controller that will be able to manage multiple component instances. It will differ slightly from a standard controller that instead of a single component reference it must use a collection.

Implement command chaining

Use promise API and wrap in calls to execute. Allow a second param in execute callback which is optional and overrides triggering on the promise then and error callbacks. We will need a pollyfill for Promise.

Create Lotus view components from x-tag components

IMPORTANT: this is not a priority item at this time, hence no milestone.

Create a Lotus view component class for all components founds at :http://www.x-tags.org/download. Use the Button view component as an example. Note that we only support lifecycle methods in the context, so there is a large amount of code that has to be refactored. For example accessors declared through x-tag would be redone using Lavender's addProperties method. See the button view component as an example.

Create Injector

Dependency Injection

//register dependency
Lotus.Context.injector.mapObject(key, constructor, useSingleton);
//calling the mapObject function with the same element or attribute key identifier overrides any existing one and logs the override.

//get dependency
Lotus.Context.injector.inject('someKey');

Revising and editing new button example

Anything you would like to add to the button example while I'm playing in the code.

~ ChangeLog

  1. Added pretty print
  2. Cleaned up the alignment on the code example

dashboard example

~ x-tag v2 dashboard

Note: V2 version is off branch.

Would like to provide a dashboard UI for examples. It would also be a great resource to have for the LotusJS project because it's written in x-tag v2 and also could be used to do some initial testing for v2 compatibility.

Issue:

Need testing for v2. It's almost complete and I need feedback on the design/arch for the build and components that are in process of being updated to v2.

V2 component updates and additions will include:
x-tabbox
x-display
x-menu
x-item
x-toggle

Question: How should the plugins for the dashboard repo for v2 beta be organized to create an intuitive build for re-usability?

Question: I feel that v2 is a very reusable library but requires a little bit of work given that, how can LotusJS leverage the new v2 version to create a more powerful API when it does come into the full release?

Question: I'm a LotusJS noob, forgive me if I get this wrong. Lotus here is creating an instance of an element that inherits the LotusJS API and x-tags adds it's lifecycle API? If so we could leverage x-tags V2 in so many more ways and think that it should be looked at more closely. Please let me know if their are requests for an x-tag v2 lifecycle extension.

var context = (function( xtag ){
  var context = new Lotus.Context(new Lavender.Config);

  context.componentMap.mapComponent('x-lotus-button', Lotus.LotusHTMLElement.prototype, Lotus.Button, xtag);

  context.componentMap.mapComponent('x-lotus-button2', Lotus.LotusHTMLElement.prototype, Lotus.Button, xtag);

            return context;
        }( xtag ));

NOTE: V2 Master branch is at least 90% done. The version on this build is of course at a different point.

~ Thanks and Regards Steve.

Create context

Context

Lotus.Context = function(){
//injector
//commandMapper
//eventBus
//componentMap
}

context = new Lotus.Context();

Add interceptor functions

Allow the central event bus to define interceptor functions. The idea would be that these functions will be called before the event call back functions are executed. Consider the following interfaces signature taken from parsley:

MessageInterceptor:
http://www.spicefactory.org/parsley/docs/2.1/api/parsley-spicelib-flex/org/spicefactory/parsley/core/messaging/receiver/MessageInterceptor.html#propertySummary

MessageProcessor:
http://www.spicefactory.org/parsley/docs/2.1/api/parsley-spicelib-flex/org/spicefactory/parsley/core/messaging/MessageProcessor.html

The MessageProcessor exposes a proceed method. I think the central event bus should check if interceptors are defined and if so it should create a new instance of a concrete MessageProcessor instance assigning all the callback functions as separate MessageInterceptor instances. The MessageProcessor would then iterate over all the MessageInterceptor instances calling the intercept method of each passing itself. The MessageProcessor's proceed method will then pop the processed MessageInterceptor from the collection and wither call the next MessageInterceptor in the collection or finish execution and call the MessageProcessor's destory method. IMPORTANT: execution of a MessageInterceptor must occur in a try catch block. Also, MessageProcessor's should also expose a cancel method which can also be called by the MessageInterceptor. This will trigger the MessageProcessor's destroy method and clean up.

Port framework to typescript

Framework info is here:
https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md

You can use decorators to do DI, scopes, etc. Info on how to build decorators is here: http://blog.wolksoftware.com/decorators-metadata-reflection-in-typescript-from-novice-to-expert-part-4

Better yet there is an IOC framework for typekit. See if you can extend it to provide the decorators for DI in typescript and hook up to function overrides that will work with Lotus injector and scopes. Framework is here: https://www.npmjs.com/package/typescript-ioc

xTag has typescript definitions created here: https://github.com/dan2dev/x-tag.d.ts/blob/master/xtag.d.ts

Lavender will be published as a typescript module. It will also be compiled as a UMD module which will replace the current minified lib in the browser.

The sample app should stay the same and be tested with the compiled libs from typescript. Alternatively you can create a typescript sample app to define all the code in the JS lib with the exception of the context, and any other code that should be truly client side POJOs, like maybe the model. This allows developers to have a clear example of how to create apps in typescript and in pure Javascript.

Create new service object for the sample application that implements promises for the service layer. It's OK to keep the existing one as an example of using responders, but include a new one that uses promises. Good article on the use of promises in typescript when targeting se5: https://blog.mariusschulz.com/2016/12/09/typescript-2-1-async-await-for-es3-es5. Also good to read: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html do a find on "Downlevel Async Functions". Shows how to set up the compiler options to include promises. Honestly I think both patterns are useful as the IResponder pattern offers an alternative to Promise pollyfills and the helper functions TypeScript generates.

Implement a component that uses CSS custom properties

Read: https://www.smashingmagazine.com/2017/04/start-using-css-custom-properties/

Basically javascript exposes two methods to effect custom properties that are used in variable definitions: .getPropertyValue() and .setProperty(). This allows you to effect the value at runtime and have the browser resolve the changes. This is a huge breakthrough that could make the use of callbacks that manually resolve model changes in view components obsolete. I'm not sure it it totally negates model bindings to effect changes int he view, but it might. I assume these custom properties would need to be assigned in the shadow DOM so they don't leak into the global scope. They should still be reachable through javascript but we have to be sure.

Add support for templates

The created method of the component map should be altered to look for a src attribute on the custom tag. If found load the partial and cache. The cache key should be based on URL not object type, this allows different tag instance to use different template definitions which could be useful to achieve polymorphism in the view. Then create a shadow root from the custom tag and append the contents of the template. This will allow our custom tags to use external layout files. Some good reading on creating a generic custom tag is here: https://www.polymer-project.org/articles/polymer-xtag-vanilla.html

Add Support for Controllers

ATM the component map injects the context directly to the view component. Add support for a controller constructor which will receive the view component instance along with the context, similar to RobotLegs controllers.

Create example application using apollo client and graphQL

Replace the rest API backend and sample service with graphQL and apollo client. Keep the sample service interface the same. Then update the service factory to return the apollo client service. This will also show how easy it is to swap out your service layer with lotus.

http://graphql.org/learn/schema/ (read first)
https://github.com/apollostack/graphql-server (graphQL server for HAPI)
https://github.com/apollostack/apollo-client
http://www.apollodata.com/optics
http://dev.apollodata.com/
https://www.youtube.com/watch?v=RaHCdBHDEhI (how to intall query)
https://www.youtube.com/watch?v=u1E0CbGeICo (overview of apollo client)

Create component map using no polyfills

IMPORTANT: this is not a priority item.

This effects Lotus.ComponentMap.prototype.mapComponent. It uses xtag specific syntax for registering a custom component. What we could do is create a default Lotus wrapper that exposes the same syntax as xtag, but just calls native DOM methods. This will replace the need for a specific framework and make the components built in Lotus more distributable in other frameworks such as React or Angular. People can load their own polyfills for the native methods. Making this the default instead of xtag will make xtag optional.

Demonstrate using Meteor's reactive data objects with a collection view

I think the best approach is to keep model collections is synch with reactive data objects. For example if we subscribed to the tasks collection as follows:

Tasks = new Mongo.Collection("tasks");
//Client code
//subscribe
Meteor.subscribe("tasks");

Then we could keep the model, which the collection view uses, in sync with the data source as follows

Tasks.find().observeChanges({
        added: function (id, fields) {
            //add the corresponding model object to the collection used by the collection view
            //use a factory and parser
            var newModelObject = someFactory.getParser(context.config).parse(fields)
            context.model.someCollection.addItem(newModelObject);
       changed: function(id, fields){
            //locate the item in the collection by id and merge the field changes
           //IMPORTANT: this requires creating a new function in the collection view to handle change events
           //We may have to do what backbone does and just re-render the view 
        },
        removed: function (id) {
            //located the item in the collection by id and remove
           for(...){
                 if( id equals id ){
                         context.model.someCollection.removeItemAt(i);
                         break;
                  }
           }
    });

This turns traditional use of a command pattern involving services and a parser on it's head, but I think that's a good thing. Meteor is all about that. We could implement a factory and parser in handling feilds and creating new model objects. This is recommended to provide a layer of abstraction between the data source and the front end client.

Create unit tests

Create unit tests for existing objects and update test config. Run and ensure all tests pass. You might be able to borrow some tests from lavenderJS as a starting point, for example tests against list classes.

Create closures for framework Objects

Lotus.CommandMap make the following read only
this.eventFunctionMap
this.instanceMap
this.context

Lotus.ComponentMap make the following read only
this.context;
this.componentInstances ;

Lotus.Injector make the following read only
this.context
this.objectMap

Create mediator map

Create abstract mediator
Create test
Create mediator map
Create test
Refactor Context.js to include a mediator map instance
Refactor test
Refactor Lotus.ComponentMap.prototype.addComponent to call this.context.mediatorMap.apply(tagInstance.tagName, tagInstance.lotusComponentInstance)
Add mediator example to button.html example

Create a package manager

Need one tool to package components. This needs to include a manifest (package.json) that details dependencies. Not sure if we can/should just use npm or not.

Need a package installer that can install components (including dependencies) by name. Again, may just be able to use npm.

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.