Git Product home page Git Product logo

frets's Introduction

FRETS (Functional, Reactive, entirely TypeScript)

An Ultralight Composable Frontend TypeScript Web Framework

GitHub Workflow Status (master) npm version Libraries.io dependency status for latest release install size

FRETS logo

A chainable API of programmer-friendly abstractions for building User Interfaces without HTML Templates or JSX. Enjoy the safety and productivity of TypeScript in your UI code.

Getting Started

npm install --save frets

Use the Starter Project to get going quickly.

Read the docs!

The basic SAM (State Action Model) app lifecycle: Action (event) -> Model (update) -> State (calculate) -> View (render) -> [wait for client events that call an Action()]

Note:

Loosely based on sam.js.org

Philosophical Rules

  1. Optimize for Developer Ergonomics (chainable APIs)
  2. No Magic Strings
  3. No Configuration Objects
  4. Encourage Some Functional Programming

Contact Me

Leave an issue here with any questions or suggestions, or reach out on twitter: @sirtimbly.

Quick Demo App

const frets = setup((f) => {
  //... register MODELS
  //... register VIEWS
    //... inside your views, register ACTIONS and FIELDS
});

// customize the state calculation function that gets called before every re-render
frets.stateRenderer = (newProps: TodoListProps, oldProps: TodoListProps): TodoListProps => {
  // add your derived state business logic here
  return newProps
};

// mount it to the DOM
frets.mountTo("main")

Read the docs for more details about getting started!

What and Why?

FRETS is a set of classes and interfaces to make it easy for you to write code that complies (mostly) with the SAM pattern. You can get all the reassurance of reliable code completion and type checking while still writing "pure" render functions. I think classes made up of functions are a perfectly valid way of giving developers the convenience of automatic code completion, and the other advantages of the Typescript tooling world. Making a developer remember all the variable names or massive copy-pasting is the enemy of clean bug-free code.

Functional Rendering with TypeScript Helper Classes

Rendering with a plain hyperscript function - like the h() function provided by maquette is powerful - but most develoers aren't sadists - so they replace it with TSX, JSX or another template compiler. I usually wouldn't bet against HTML, but JSX has always just felt kinda annoying to me. It differs from HTML in a few important ways. And HTML isn't really that great for developers to begin with.

As a web developer I would recommend against any abstractions that pull us too far away from HTML/CSS. I've seen the pain of ASP.Net WebForms and Adobe Flex. Yet, here I am recommending developers use an abstraction that breaks fundamental rules about applying HTML and CSS to your site.

I created the FRETS-Styles-Generator tool to read a CSS file and turn it into a TS class that proxies the functionality of the maquette h() function. Normally you would pass your classnames to it like so h("div.card.content-area", ["my content"]). The css selector is a big glaring ugly magic string, it's not refactorable or type checked. So with the generated class now you can express the above function like this $.div.card.contentArea.h(["my content"]). If you choose to run the generator against one of the several "atomic css" libraries out there then you get a whole bunch of composable classes that let you specify your your UI looks, what it's nested structure is, and encourages thorough refactoring within your TypeScript. Bonus, the generator runs everything through PostCSS so you can split up your stylesheets in to modules that you can @import and make use of new CSS variable.

Bigger bonus, if you go into your stylesheets and start removing classes, the generator will rebuild the TypeScript class and your TS project will give you build errors if any of your CSS is now missing! That could make maintenance of app stylesheets a lot easier.

License

FOSSA Status

frets's People

Contributors

dependabot[bot] avatar sirtimbly avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

fossabot

frets's Issues

Deprecate the old App class?

I've switched to using the new FRETS class which allows developers to build up the app in a more readable and easy to parse set of functions and assignments like:

  • registerAction(...)
  • registerView(...)
  • calculator = ...
  • validator = ...

I would like to deprecate the old Classes including:

  • App
  • AppBuilder
  • AppConfiguration
  • Actions
  • Model
  • State
  • View
  • ComponentMap

My idea is to target them to be gone in 1.0

This would allow enforcement of stricter code coverage rules.

Add plugin registration feature

I want it to be possible to register new functionality in a simple developer friendly and async way.

Developers should be able to include a UI rendering function that takes an instance of the FRETS class and enhance it with it's own necessary actions, data properties, and calculation and validation functionality.

Approach: Add a third parameter to the standard UI rendering function signature, an instance of the FRETS class. When a plugin needs to be registered, all of it's action methods are added to the actions of the FRETS instance. A calculate function can be patched with a new function (maybe one that calls next() at the end?). The validate function can be patched in the same way. The data properties class can be extended with additional properties. When the App is extended with this plugin the extend/register function should return a new instance of the props and the actions classes so that developers still get great intellisense and strong typing when accessing "actions" or "props" internally.

Research this approach: https://hapijs.com/tutorials/plugins

Things to avoid:

  • adding internal state to the rendering function which is expected to persist between renders.
  • making the developer remember if something is available on the props or actions through magic strings or magic property names that can't be hinted from the IDE.

Figure out a method for doing simple input-updater actions

When a developer is building a form they find themselves wanting to update state to match whatever is in a HTML Input element value all the time. Developers need an easy way to generate simple state update action functions like the following in a non-repetetive way... preferrably without magic strings.

F.actions.setName = F.registerAction((e: Event, props: AppProps): AppProps => {
  props.customerName = (e.target as HTMLInputElement).value;
  return props;
});

But it would looks something like this when implemented:

$.input.h({ 
  name: "email" 
  value: props.email,
  onchange: actions.getInputUpdater("email"),
}),

Write a webpack loader that converts styles types function chains back into strings

In order to save on final compiled bundle size the generated classes based on the CSS files should be circumvented after the developer is done using them for writing. A webpack loader might be able to look at the long function chains and use regex to convert them back into a static hyperscript declaration like h("div.bold.mx-2.p-2") the typescript classes are super useful for writing and linting, but add a couple kilobytes of code to the final bundle just for developer ergonomics .

I have considered switching to CSS Modules but am not sure it will really be helpful since the webpack css-loader is built for replacing values in a class attribute with a specifically scoped hashed version.

Pseudo code:

  1. loader function is passed the source of a typescript file
  2. check for existence of an import of a generated atomic stylesheet class
  • this relies on consistent use of a naming convention like import $$ from "./base-styles" so anything that start with import {$, $$ is a style class import
  1. regex match any code string that starts with $$( or $. and ends with .h(
  2. each match should be converted so that dots are spaces, and camelCased symbols are converted to hyphen-cased - basically the inverse of what the frets-styles-generator does to create the typescript class members
  3. remove the reference to the style class import
  4. add a import { h } from "maquette" (if it doesn't already exist)

Add the friendly navigation setup code from frets-starter

The way of adding top level screens as a list to the props and the actions in frets-starter is pretty clean, but some of it could be brought into the main frets library as an optional module.

Something like:

import {registerNavigation} from "frets"


export interface IRouteKeys {
  About: SampleScreens;
  Home: SampleScreens;
  Users: SampleScreens;
}

export const routeKeys: IKeyObject = {
  Home: "/home",
  Users: "/users",
  About: "/about",
};

F = registerNavigation<MyProps, MyActions>(myApp, routeKeys)

WIP: Interceptors and Contect changes

Inspired by the documentation on tao.js.org I am re-considering the validator flow. A rendering method should be able to register interceptors for validation. If an interceptor returns a falsy value then execution continues, if it returns any value that value is treated as a new context for the state to calculate.

Users who write rendering functions should be able to create adhoc state calculations as well. They should be registered in the main app and they just affect values in their own purview, some key perhaps that identifies the terms to be calculated and affected.

...

Startup Documentation

A smaller and easier to read introduction is needed for the main README. Currently, it doesn't do a good job of easing new users into the library.

Add a patch method for rendering with just a subset of changed properties

  • I'm submitting a ...
    [ ] bug report
    [x] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Summary
    When an async promise is resolved its necessary to call F.render(props) and this means there is a chance that old state props will be sent into the app. A patch method that just overwrites a portion of the main props object would avoid this problem.

Feature: Make the rendering library more customizable

  • Summary
    If we could use other html rendering and VDom implementations it would make this library more flexible and reusable. Integrated with other communities. Maybe as a first pass we see if it's possible to refactor the rendering functions to support switching to https://redom.js.org/. Another super-light-weight dom library.

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.