Coder Social home page Coder Social logo

surplus's People

Contributors

adamhaile avatar ismail-codar avatar qix- avatar tinchoz49 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

surplus's Issues

Jsx spread attributes

Todomvc was very useful for me. Thanks for @adamhaile

Surplus and S.js made me very excited. I think this is the best frontend component library for me and others. It allready fastest

I found very few bugs after recent commits.

In the example below App1 and App2 must be the same.
https://facebook.github.io/react/docs/jsx-in-depth.html#spread-attributes

function App1() {
    return <Greeting firstName="Ben" lastName="Hector" />;
}

function App2() {
    const props = { firstName: 'Ben', lastName: 'Hector' };
    return <Greeting {...props} />;
}

Here is the rendering result. App2 rendering incomplete.

function App1() {
    return Greeting({
        firstName: "Ben",
        lastName: "Hector",
        children: [
            
        ]});
}
function App2() {
    var props = { firstName: 'Ben', lastName: 'Hector' };
    return Greeting({
        
        children: [
            
        ]});
}

Handling typescript generic parameters

This works

const Footer = (props: { leftCount, children?}) => {
    return <div>{props.leftCount()}</div>
}

But this is not working. Error: element missing close tag at line......
DataSignal<number> is confused with tags.

const Footer = (props: { leftCount:DataSignal<number>, children?}) => {
    return <div>{props.leftCount()}</div>
}

No attributes are actually created?

Given something like this:

return <div class={ $style.host } {...attr}>
        <div class={ $style.callout__content }>
            <svg version="1.1" class={ $style.callout__quotation } x="0px" y="0px" viewBox="0 0 48 42.1" hidden={ hideQuotes }>
                <polygon points="48,0 48,11.6 39.6,19.5 48,19.5 48,42.1 27.5,42.1 27.5,19.7" />
                <polygon points="20.5,0 20.5,11.6 12,19.5 20.5,19.5 20.5,42.1 0,42.1 0,19.7" />
            </svg>
            <p class={ $style.callout__copy + ' d3' }>
                { children }
            </p>
        </div>
    </div>

The actual output is:

return (function () {
            var __, __div1, __div1_svg1, __div1_svg1_polygon1, __div1_svg1_polygon2, __div1_p2, __div1_p2_insert1;
            __ = createElement('div');
            __div1 = createElement('div');
            __div1_svg1 = createElement('svg');
            __div1_svg1.version = "1.1";
            __div1_svg1.x = "0px";
            __div1_svg1.y = "0px";
            __div1_svg1.viewBox = "0 0 48 42.1";
            __div1_svg1_polygon1 = createElement('polygon');
            __div1_svg1_polygon1.points = "48,0 48,11.6 39.6,19.5 48,19.5 48,42.1 27.5,42.1 27.5,19.7";
            appendChild(__div1_svg1, __div1_svg1_polygon1);
            __div1_svg1_polygon2 = createElement('polygon');
            __div1_svg1_polygon2.points = "20.5,0 20.5,11.6 12,19.5 20.5,19.5 20.5,42.1 0,42.1 0,19.7";
            appendChild(__div1_svg1, __div1_svg1_polygon2);
            appendChild(__div1, __div1_svg1);
            __div1_p2 = createElement('p');
            __div1_p2_insert1 = createTextNode('');
            appendChild(__div1_p2, __div1_p2_insert1);
            appendChild(__div1, __div1_p2);
            appendChild(__, __div1);
            __div1_svg1.class =  $style.callout__quotation ;
            __div1_svg1.hidden =  hideQuotes ;
            insert({ start: __div1_p2_insert1, end: __div1_p2_insert1 },  children );
            __div1_p2.class =  $style.callout__copy + ' d3' ;
            __div1.class =  $style.callout__content ;
            __.class =  $style.host ;
            S(function (__state) { return (attr)(__, __state); });
            return __;
        })()

With this output, no attributes are actually created. This seems completely contrary to the documentation which is that a) "Surplus allows class and for as aliases for the className and htmlFor properties", which doesn't seem to happen (it sets an arbitrary class property), and b) there's no real support for SVG, which does not have hyphenated properties. If only hyphenated values get turned into attributes, how is that useful at all?

idea/question: Handlebars as alternative to JSX?

Hi. Your project looks really great. The S package is super performant -- congrats!

We're somewhat stuck on the Handlebars approach to templating, and were wondering if your architecture support Handlebars instead of/in addition to JSX for the templates?

For example, incremental-bars implements Handlebars but uses incremental-dom as its backend, instead of strings.

But I like your approach of going straight to the DOM. It feels similar to Ractive , which has the Handlebars goodness, but, alas, not the speed of S.

I guess one possibility would be for Ractive to leverage S in place of its current data/computed model? But another possibility would be for Surplus to offer Handlebars support.

Of course, we could just learn JSX instead... :-)

Adding spread support

Surplus' original syntax did not have spread expressions, but to support easy transition to/from JSX, I intend to add them.

Three's a design question regarding how spreads should work in a "real DOM" scenario: spreads make perfect sense with vdom, as we're constructing a property object, which spread expressions extend. But in surplus, we're building real DOM nodes and setting their properties directly, so there is no property object to start with.

Another design question is how to support what were previously called "mixins" in Surplus: expressions that return a function which is then called with the current node.

The intended design is to support both vdom-style property objects and mixin-style functions by:

  1. providing runtime support to translate vdom-style property objects into node property sets. This will likely be a new method, Surplus.spread(...). It will need to account for runtime property name translation, to support things like onClick -> onclick, which is currently done at compile time.

  2. supporting mixins by making Surplus.spread(...) check whether it's being passed a property object (vdom-like translation) or a function (mixin-style call-with-node).

Tasks:

  • spread objects for DOM element expressions
  • spread functions (mixins) for DOM element expressions
  • spread objects for subcomponent expressions
  • spread functions (mixins) for subcomponent expressions

A problem with fns

An example with 0.5.0-beta1:

  const dv = (
    <div fn={() => {}}>
      1<br />
      2<br />
    </div>
  );
  console.log(dv.outerHTML); //  output is <div>      1</div>

But same example without fn it outputs correctly.

  const dv = (
    <div>
      1<br />
      2<br />
    </div>
  );
  console.log(dv.outerHTML); //  output is <div>      1<br>      2<br></div>

question: computations and input typing

Hi there,

I just found something different from mithril.js! It's probably by design but I would like your suggestion for a workaround...

I have a input field, normal fn={ data(Svalue) }, and it has some computations to show a error message underneath it when it's empty.
Using Surplus, after something is entered on the input field the message only goes away after some change in focus.
Using mithiril.js the message goes away when the first character is entered.

Is there a way to make Surplus behave like mithril.js?

Thanks, Grieb

Dynamic attributes vs static attributes

Hi @adamhaile only small problem in beta3 npm install && npm run build & npm run test cannot be work from fresh copy. PR is not required please add "s-js": "^0.4.5" and as dependencies and
"jasmine-core": "^2.6.0" to devDependencies in package.json.

Besides about this issue here is an example:

<input
        className={style({ border: 'none' })} //it uses https://typestyle.github.io/#/core/-style-
        type="text"
        onInput={updateInputData}
        value={props.value()}
      />

Current output:

    __input1 = Surplus.createElement('input', null, __);
   Surplus.S(function () {
        __input1.className = lib_1.style({ border: 'none' });
        __input1.type = "text";
        __input1.oninput = updateInputData;
        __input1.value =props.value();
    }

className and onInput is looks dynamic if we look at it by JSX but it is static if we look at by surplus. Only value={props.value()} is dynamic. type="text" is hardly static.

Correct output may be:

    __input1 = Surplus.createElement('input', null, __);
   __input1.className = lib_1.style({ border: 'none' });
   __input1.type = "text";
   __input1.oninput = updateInputData;
   Surplus.S(function () {
        __input1.value =props.value();
    }

export 'appendChild' (imported as 'Surplus') was not found in 'surplus'

I'm trying to run the example near the top of the README. Getting this error:

WARNING in ./src/index.jsx
45:16-35 "export 'appendChild' (imported as 'Surplus') was not found in 'surplus'
 @ ./src/index.jsx
 @ multi (webpack)-dev-server/client?http://localhost:8080 webpack/hot/dev-server ./src/index.jsx

surplus is beta version ^0.5.0-beta6 (no problem on 0.4). My code:

import * as Surplus from 'surplus';
import S from 's-js';
import SArray from 's-array';

if (module.hot) {
  module.hot.accept();
}

var Todo = t => ({               // our Todo constructor
  title: S.data(t.title),   // properties are S data signals
  done: S.data(t.done)
}),
todos = SArray([]),          // our todos, using SArray
newTitle = S.data(""),       // title for new todos
addTodo = () => {            // push new title onto list
  todos.push(Todo({ title: newTitle(), done: false }));
  newTitle("");             // clear new title
},
view = S.root(() => {                      // declarative main view
  <div>
     <h2>Minimalist ToDos in Surplus</h2>
     <input type="text" fn={data(newTitle)}/>
     <a onClick={addTodo}> + </a>
     {todos.map(todo =>     // insert todo views
        <div>
           <input type="checkbox" fn={data(todo.done)}/>
           <input type="text" fn={data(todo.title)}/>
           <a onClick={() => todos.remove(todo)}>&times;</a>
        </div>
     )}
  </div>;
});

document.body.appendChild(view); // add view to document

Unidirectional dataflow doesn't work well

I see surplus-fn-data does 2 way bindings, but I don't think that way. I tried to do unidirectional dataflow with surplus.

const Name = (props) => {
  const {name, onchange} = props
  return <input type="text" value={name} onKeyUp={onchange} />
}

const name = S.data('');
function updateName({ target: { value } }) {
  name(value);
}
const view = S.root(() => (
  <div>
    name:{name()}
    <br />
    <Name name={name()} onchange={updateName} />
  </div>
));
document.body.appendChild(view);

loss-focus

Problem with this code is after I type a character into the input, it loses focus. I think what is happening is the view is being regenerated and a new input field is replacing the old one. vdom solves this by patching....

Is this just how surplus works, or can I do unidirectional dataflow?

Compiler docs

Could you explain a bit what does the compiler do, why is it needed, etc?

fast (but not that fast) input misses last key press

Hi there, got some time to work on my little project again... ๐Ÿ˜‰
And I think I found some kind of a bug!

With this code (boilerplate removed):

<input type="text" fn={ data(login.user, "keyup") } />
<input type="password" fn={ data(login.password, "keyup") } />

If I type admin as user and then use the tab key I get only admi in login.user, it happens in more than half the tries (if you just type without pauses). The input field has the full admin though.

Thanks, Grieb

compiler broken

> require('surplus');
{ insert: [Function: insert$$1],
  S:
   { [Function: S]
     root: [Function: root],
     on: [Function: on],
     data: [Function: data],
     value: [Function: value],
     freeze: [Function: freeze],
     sample: [Function: sample],
     cleanup: [Function: cleanup] },
  createElement: [Function: createElement],
  createComment: [Function: createComment],
  createTextNode: [Function: createTextNode],
  appendChild: [Function: appendChild] }
> require('surplus/compiler');
Error: Cannot find module 'surplus/compiler'
    at Function.Module._resolveFilename (module.js:536:15)
    at Function.Module._load (module.js:466:25)
    at Module.require (module.js:579:17)
    at require (internal/module.js:11:18)
    at repl:1:1
    at ContextifyScript.Script.runInThisContext (vm.js:50:33)
    at REPLServer.defaultEval (repl.js:240:29)
    at bound (domain.js:301:14)
    at REPLServer.runBound [as eval] (domain.js:314:12)
    at REPLServer.onLine (repl.js:441:10)
> require('surplus/compiler');
Error: Cannot find module 'surplus/compiler'
    at Function.Module._resolveFilename (module.js:536:15)
    at Function.Module._load (module.js:466:25)
    at Module.require (module.js:579:17)
    at require (internal/module.js:11:18)
    at repl:1:1
    at ContextifyScript.Script.runInThisContext (vm.js:50:33)
    at REPLServer.defaultEval (repl.js:240:29)
    at bound (domain.js:301:14)
    at REPLServer.runBound [as eval] (domain.js:314:12)
    at REPLServer.onLine (repl.js:441:10)
>
```

Questions about content.ts

When I look through both the generated code for the minimalist ToDos example in CodePen [with help from console.log(comple(...))] and surplus-todomvc (https://github.com/adamhaile/surplus-todomvc/blob/master/dist/main.js) I do not see where the code from content.ts would actually be used. Any pointers?

I also noticed an internal reconcileArrays function in content.js, unfortunately do not really understand the purpose; wondering if it may suffer from VDOM-like performance issues under certain circumstances. It would be great if you can explain this a little and maybe document how we can avoid this? Any chance we can move the array processing into another module or perhaps find a way to get rid of it?

0.5 beta packages now on npm

Published under the @beta tag, so get them via npm install surplus@beta.

Still some punchlist items to go (#13), plus testing. Any testing help welcome!

Component life cycle events strategy

In the virtual dom environment component life cycle events can be easier. But surplus is different. At least component attached to DOM and component removed from DOM events is required in real world.

I have developed a solution for now but mozilla this page says it unperformed. Because DOMNodeRemoved is deprecated and performance problematic. https://github.com/SilentCicero/throw-down is may be another solution.

I think it would be better to solve this problem in the surplus infrastructure.

I'm doing some material design components experiments right now.

My current solution
import * as Surplus from 'surplus';
Surplus;

const lifeCycle = (options: { created: (node: HTMLElement) => void, removed: (node: HTMLElement) => void }): (node: HTMLElement) => void => {
    return (node) => {
        setTimeout(() => {
            options.created.call(options, node)
            node.parentElement.addEventListener("DOMNodeRemoved", (e) => {
                options.removed.call(options, node)
            })
        })
    }
}

const buttonCreated = (node: HTMLElement) => {
    this.ripple = window["mdc"].ripple.MDCRipple.attachTo(node);
}

const buttonRemoved = (node: HTMLElement) => {
    this.ripple.destroy();
}


export const MdButton = (props) => {
    return <button className="mdc-button mdc-ripple-surface" {...lifeCycle({ created: buttonCreated, removed: buttonRemoved }) }>
        {props.children[0]}
    </button>
}

Links:
Material design components
Angular example

Compression

I really like the concept of this library! Great work!

One thing that came to mind was that the inline dom statements might quickly add up because they're not minified. Is that something to worry about? If so is there a way to wrap those in minify-able helper functions?

Example minified code snippet of the TODO MVC:

c=function(n){return function(){var e,c,s,f,d,p,h,m,v,g,w,y,C,E,S,x,b,N,T,k,A,O,L,M,P,R,j,B,F,_,z;return e=r.createElement("section"),c=r.createElement("section"),c.className="todoapp",s=r.createElement("header"),s.className="header",f=r.createElement("h1"),f.innerText="todos",r.appendChild(s,f),d=r.createElement("input"),d.className="new-todo",d.placeholder="What needs to be done?",r.appendChild(s,d),r.appendChild(c,s),p=r.createElement("section"),p.className="main",h=r.createElement("input"),h.className="toggle-all",h.type="checkbox",r.appendChild(p,h),m=r.createElement("label"),m.htmlFor="toggle-all",m.innerText="Mark all as complete",r.appendChild(p,m),v=r.createElement("ul"),v.className="todo-list",g=r.createTextNode(""),r.appendChild(v,g),r.appendChild(p,v),r.appendChild(c,p),w=r.createElement("footer"),w.className="footer",y=r.createElement("span"),y.className="todo-count",C=r.createElement("strong"),E=r.createTextNode(""),r.appendChild(C,E),r.appendChild(y,C),r.appendChild(y,r.createTextNode(" item")),S=r.createTextNode(""),r.appendChild(y,S),r.appendChild(y,r.createTextNode(" left")),r.appendChild(w,y),x=r.createElement("ul"),x.className="filters",b=r.createElement("li"),N=r.createElement("a"),N.href="#/",N.innerText="All",r.appendChild(b,N),r.appendChild(x,b),T=r.createElement("li"),k=r.createElement("a"),k.href="#/active",k.innerText="Active",r.appendChild(T,k),r.appendChild(x,T),A=r.createElement("li"),O=r.createElement("a"),O.href="#/completed",O.innerText="Completed",r.appendChild(A,O),r.appendChild(x,A),r.appendChild(w,x),L=r.createElement("button"),L.className="clear-completed",L.innerText="Clear completed",r.appendChild(w,L),r.appendChild(c,w),r.appendChild(e,c),r.appendChild(e,r.createTextNode(",        ")),M=r.createElement("footer"),M.className="info",P=r.createElement("p"),P.innerText="Double-click to edit a todo",r.appendChild(M,P),R=r.createElement("p"),R.innerText="Template by ",j=r.createElement("a"),j.href="http://sindresorhus.com",j.innerText="Sindre Sorhus",r.appendChild(R,j),r.appendChild(M,R),B=r.createElement("p"),B.innerText="Created by ",F=r.createElement("a"),F.href="https://github.com/adamhaile",F.innerText="Adam Haile",r.appendChild(B,F),r.appendChild(M,B),_=r.createElement("p"),_.innerText="Part of ",z=r.createElement("a"),z.href="http://todomvc.com",z.innerText="TodoMVC",r.appendChild(_,z),r.appendChild(M,_),r.appendChild(

Warning: breaking change to props.children

As the last (I believe) breaking API change before 0.5 goes to release, I've changed the behavior of the props.children property passed to subcomponents. Previously, Surplus always created an array with as many elements as there were children. Now it behaves like props.children in React, where if there are no children, props does not contain a 'children' property, if there is one, then it contains that one child, and if there are more than one, then it contains an array of all children.

<Foo />                     // props has no 'children' property
<Foo>1</Foo>                // props.children === 1
<Foo>1<span>2</span>3</Foo> // props.children === [1, <span>2</span>, 3]

This has the advantage of a) being consistent with React, b) being consistent with all React-conforming tools, like Typescript, which expect this behavior, c) avoiding creating an array when not strictly necessary. The downside is that props.children becomes polymorphic, in that it may be undefined, a single element, or an array.

Also, previously any 'children' attribute took precedence over the actual JSX children, so <Foo children={1}>2</Foo> would resolve to props.children === 1. Now precedence is like React, in that JSX children take precedence, and props.children === 2.

Typescript JSX output is incompatible with html tags in regex expressions

Hello!
I've recently started using Surplus and it's really great! But I think I've found a bug in the compiler:

// controller.ts
export function update(text : string) : string {
	return text.replace(/<br>+/g, '\n');
}
// view.tsx 
export const TestEditor = () => {
	const text = S.data('Hello'),
	onkeyup = ({ target: { value } } : any) => {
		text(clean(value));
	};
	return (
		<div> 
			Text is: {text()}
		<input value={text()} onKeyUp={onkeyup} /> 
		</div>
	);
}
// main.ts
S.root(() => {
        const testEditor = TestEditor();
        document.body.insertAdjacentElement('afterbegin', testEditor);
    });

This produces the error:
ERROR in ./public/js/controller.ts
Module build failed: Error: element missing close tag at line 19 col 25: ``
+/g, '\n');}
''
at ERR (/node_modules/surplus/compiler/index.js:354:15)
at jsxElement (/node_modules/surplus/compiler/index.js:162:17)
at program (/node_modules/surplus/compiler/index.js:91:31)
at parse (/node_modules/surplus/compiler/index.js:83:12)
at Object.compile (/node_modules/surplus/compiler/index.js:1444:45)
at Object.preprocess (/node_modules/surplus-loader/index.js:7:33)
@ ./public/js/main.ts 3:7-49

I think the problem lies in the surplus-loader. Compiling the regex with Babel works fine when I test that!
Edit: I'm running the 0.5 beta of surplus-loader.

Question: Any ability to use Surplus behind Getter/Setters?

Even though I recognize the performance hit I was trying surplus behind ES5 Getter/Setters and ES6 Proxies and I noticed certain dependencies would just get skipped. It looked like the compiled templates would be slightly different whether the parenthesis were there or not.

I'm probably doing something terribly bad to performance by trying this. I just come from a Knockout background (used in production for about 7 years, training new devs etc). It's been clear to me for quite some time that while fine grained change dependency tracking is the superior approach(not knockout specifically) the api/syntax and the need to keep track of nested mapping prevents it from ever gaining a ton of traction. MobX the other large library with this sort of approach is still typically used with React and trapped by the fact updates are still per component, making it always limited by React and hence slower than it.

It's my belief that while having explicit computational wrappers is a must here, in the whole scheme of larger projects it's much cleaner than tracing through layers of shouldComponentUpdate and other very procedural lifecycle functions. However having data look like anything but POJO's is unacceptable.

I was very happy to have discovered this project 6 months back as none of my approaches I've worked on the last couple years beat out the fastest virtual dom ones across the board but I knew it should be possible. While it's likely the overhead of both the Surplus syntax and the proxying is less performant than just putting the logic in the proxying method itself, I was curious to try it for comparison sake.

In any case this project has been an inspiration and I learn something every time I look at the source code.

if/else or ternary statements

This doesn't work.

export interface Greeting {
    msg: string
}
export const GreetingV = ({pojo}: {pojo: Greeting}) =>
<div>
  <b>{pojo.msg}</b>
</div>

export const HelloV = ({pojo}: {pojo: Greeting}) =>
<div>
  <span>{pojo.msg}</span>
</div>
<div>
${p.disabled ? <HelloV pojo={p.greeting} /> : <GreetingV pojo={p.greeting} />}
</div>

Would also be cool if the compiler lazily creates it (and caches) on the expression that evals to true.

data binding on 0.5 beta

Hi there, nice approach, I'm just trying it out...

After some trouble to start I made the example in the README of S.js work except by the &times;, it shows exactly like that and not as "ร—".

Anyway, I went forward and moved to 0.5 beta and now the data bindings for the inputs are not working anymore!!! the only change was the library version!

Thanks, and again, congratulations! I hope it does thrive!

  1 import S from 's-js';                                                          
  2 import SArray from 's-array';                                                  
  3 import * as Surplus from 'surplus';                                            
  4 import data from 'surplus-mixin-data';                                         
  5                                                                                
  6 var Todo = t => ({               // our Todo constructor                       
  7        title: S.data(t.title),   // properties are data signals                
  8        done: S.data(t.done)                                                    
  9     }),                                                                        
 10     todos = SArray([]),          // our array of todos                         
 11     newTitle = S.data(""),       // title for new todos                        
 12     addTodo = () => {            // push new title onto list                   
 13        todos.push(Todo({ title: newTitle(), done: false }));                   
 14        newTitle("");             // clear new title                            
 15     },                                                                         
 16     view = S.root(() =>                                                        
 17        <div>                     // declarative main view                      
 18           <input type="text" {...data(newTitle)}/>                             
 19           <a onClick={addTodo}>+</a>                                           
 20           {todos.map(todo =>     // insert todo views                          
 21              <div>                                                             
 22                 <input type="checkbox" {...data(todo.done)}/>                  
 23                 <input type="text" {...data(todo.title)}/>                     
 24                 <a onClick={() => todos.remove(todo)}>&times;</a>              
 25              </div>)}                                                          
 26        </div>);                                                                
 27                                                                                
 28 window.onload = () => {                                                        
 29   document.body.appendChild(view); // add view to document                     
 30 };                                                                             
  1 import minify from 'rollup-plugin-babel-minify';                               
  2 import nodeResolve from 'rollup-plugin-node-resolve';                          
  3 import surplus from 'rollup-plugin-surplus';                                   
  4                                                                                
  5 export default {                                                               
  6   entry: 'main.js',                                                            
  7   dest: 'bundle.js',                                                           
  8   'format': 'iife',                                                            
  9   'plugins': [                                                                 
 10     surplus(),                                                                 
 11     process.env.ENV === "production" ? minify({                                
 12       "comments": false,                                                       
 13       "sourceMap": false,                                                      
 14     }) : undefined,                                                            
 15     nodeResolve(),                                                             
 16   ].filter(x => x !== undefined),                                              
 17 }                                                                              

"attributes is not a function"

I have this object

const attributes = { "slash-color": "light", "text-color": "dark"}
return <div class={ $style.host } {...attributes}>...</div>

However, Surplus throws the error in the browser "attributes is not a function". Is this because it has to be S.data? In the example I see:

var props = { type: "text" },
    input3 = <input {...props} />;

But this doesn't seem to work as expected.

Server-side rendering?

This project looks awesome, and I came across it by looking at js-frameworks-benchmark. One thing that's not clear: since Surplus creates native DOM elements, does that mean it can't pre-render an HTML page and "re-hydrate" it, like other libs (reducing time of first-paint)?

That is, other related questions: what does the root page look like? How is it attached to a document? How are these components consumed? Would be good to have some ideas of how it works in practice. Thanks.

text nodes with useless spaces/blanks

Hi @adamhaile, yes, me again... ๐Ÿ˜œ

Anyway, now that you have support for html entities it would be nice to remove all useless spaces and blanks (like new lines) from text nodes.

An example:

[...]
      <label className="form-checkbox">
        <input type="checkbox" fn={ data(user.enabled) } />
        <i className="form-icon" />
        Active
      </label>
[...]

Generates something like:

[...]
    __div2_form2_div1_div3_label1 = createElement('label', "form-checkbox", __div2_form2_div1_div3);
    __div2_form2_div1_div3_label1_input1 = createElement('input', null, __div2_form2_div1_div3_lab);
    __div2_form2_div1_div3_label1_i2 = createElement('i', "form-icon", __div2_form2_div1_div3_label1);
    createTextNode('\
                   Active\
              ', __div2_form2_div1_div3_label1);
[...]

And "minifyed" gives something like:

[...]A=r("label","form-checkbox",y),B=r("input",null,A),D=r("i","form-icon",A),s("              Active            ",A)[...]

Is it clear what I mean? At least the spaces at the beginning and end of the string should be remove as they serve nothing in the html and in the source they are only for readability. Ideally all sequences of multiple spaces could be reduced to a single space if not at the beginning nor end.

There is a exception though, <pre> tags... Or is there some other reason not to remove spaces?

If you would like I can have a go to implement this...

Thanks,
Grieb.

es6 template literal interpreted as jsx by compiler

First of all this is a really cool project, thanks for making it open source. I think I may have stumbled upon a bug in the compiler.

I am trying to put together a SSR'd surplus project using undom and I use es6 template literal in the render.jsx that I am passing to the surplus-loader. I am going to work around this by making the template literal into a function and moving it into it's own file, so it's not a show stopper but I thought you might be interested to know about this behavior none the less. Below is the error message I got from the build.

ERROR in ./src/server/render.jsx
Module build failed: Error: unexepected value for JSX property at line 118 col 17: ``en>       <head>
          <m''
    at ERR (/mnt/data/Work/github/andyrj/idiom/node_modules/surplus-preprocessor/index.js:443:15)
    at property (/mnt/data/Work/github/andyrj/idiom/node_modules/surplus-preprocessor/index.js:311:20)
    at htmlElement (/mnt/data/Work/github/andyrj/idiom/node_modules/surplus-preprocessor/index.js:217:33)
    at codeTopLevel (/mnt/data/Work/github/andyrj/idiom/node_modules/surplus-preprocessor/index.js:185:31)
    at parse (/mnt/data/Work/github/andyrj/idiom/node_modules/surplus-preprocessor/index.js:177:12)
    at Object.preprocess (/mnt/data/Work/github/andyrj/idiom/node_modules/surplus-preprocessor/index.js:846:45)
    at Object.preprocess (/mnt/data/Work/github/andyrj/idiom/node_modules/surplus-loader/index.js:7:37)
 @ ./src/server/index.js 13:0-30
 @ multi ./src/server

And here is a snippet from the code causing the above in the compiler.

    const route = router.match(path);
    const View = route.component;

    document.body.appendChild(<View params={route.params} />);

    /* eslint-disable */
    const html = `
      <!DOCTYPE html>
      <html lang=en>
       <head>
          <meta charset="utf-8" />
          <meta name="referrer" content="origin" />
          <meta http-equiv="X-UA-Compatible" content="IE=edge" />
          <meta name="viewport" content="width=device-width, initial-scale=1" />
          <title>${meta.title}</title>
          <link rel="shortcut icon" href="/favicon.ico" />
          <link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Roboto+Slab:400,700|Material+Icons" />
          <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css" />        
          ${styles}
          ${meta.reduce((acc, val) => acc + val)}
          <script src='${vendor}' defer></script>
          <script src='${main}' defer></script>
          <script id="state" type="application/json">${JSON.stringify(state)}</script>
        </head>
        ${serialize(document.body)}
      </html>`;

Maybe do not evaluate elements.

First, thanks for sharing. I'm very appreciate and use your code in own projects.
Now, JSX compiler emits nodes with perfectly readable code

view = function () {
    var __, ...
   __ = createElement(...
    ...
    return __;
}()      // here it call function end evaluate element. I'd like to remove this call.

I can mount view document.body.appendChild(view) only once because of DOM rules

If the given child is a reference to an existing node in the document, appendChild() moves it from its current position to the new position (there is no requirement to remove the node from its parent node before appending it to some other node).

Maybe will be meaningful to leave view() been a function but not evaluate it. This allow instantiate document.body.appendChild(view()) any times needed. Also it allows to easily write functional components
const Component = ((props) => <div>{props.content}<div>)
and use em like a template

var view = <div>
<Component content={S(some_computation)}>
</div>;

If you agree this can make sense, than I can pull request, or you can do it yourself - just remove )() call in compiler's codegen(). I done it and test in my local repo and things work as expected.

Surplus doesn't handle multi-line elements

The Surplus parser failed with this HTML:

            <svg version="1.1"
                class={ $style.callout__quotation }
                x="0px"
                y="0px"
                viewBox="0 0 48 42.1"
                hidden={ hideQuotes }
            >
                <polygon points="48,0 48,11.6 39.6,19.5 48,19.5 48,42.1 27.5,42.1 27.5,19.7" />
                <polygon points="20.5,0 20.5,11.6 12,19.5 20.5,19.5 20.5,42.1 0,42.1 0,19.7" />
            </svg>

I discovered that it couldn't handle properties on multiple lines, which was unexpected. The following parsed fine:

            <svg version="1.1" class={ $style.callout__quotation } x="0px" y="0px" viewBox="0 0 48 42.1" hidden={ hideQuotes }>
                <polygon points="48,0 48,11.6 39.6,19.5 48,19.5 48,42.1 27.5,42.1 27.5,19.7" />
                <polygon points="20.5,0 20.5,11.6 12,19.5 20.5,19.5 20.5,42.1 0,42.1 0,19.7" />
            </svg>

Latest mixin usage( fns)

Hi @adamhaile now I see that {...mixin()} changes to fn={mixin}. But this version seems to not typescript friendly!

Example code:

<a fn={mixin1} fn={mixin2} />

This error can be fix with editing interface IntrinsicElements
TS2559: Type '{ fn: (el: any) => string; }' has no properties in common with type 'HTMLAttributes<HTMLAnchorElement>'.

But this seems to be a bigger problem.
TS17001: JSX elements cannot have multiple attributes with the same name

Pluggable JSX layer?

In general I think this is really awesome work and will do as much as I can (within reason) to promote it.

That said, I suspect it should be possible to take advantage of more widely used JSX processors such as Acorn JSX (https://github.com/RReverser/acorn-jsx) or perhaps Babel transform-react-jsx. More widely used, better documented, etc.

I would be happy to give it a try when I get a chance.

It would also be cool to support something like https://github.com/choojs/hyperx (which can be interpreted or compiled) or adaptation of https://github.com/hyperapp/html API for those who may want a more pure JavaScript API (maybe better for CodePen as well).

JSfiddle/CodePen examples?

Looks like an awesome library, would it be possible to provide some "hello-world" examples in CodePen or JSFiddle so you could experiment a little? Also a minified version on cdnjs that works directly in the browser?

html entities aren't being transpiled (at least on 0.5.0-beta1)

Hi @adamhaile, me again...

I have just stumbled with a situation where I need some html entities, I need to include a &nbsp; to get tings on the correct place...

I know why it doesn't work: it is considered as text and all text nodes are created with document.createTextNode(value) which does treat everything as pure text...
I tried using unicode, \u00A0, but them the slash got escaped as it should be... If I insert the unicode char it simple vanishes during compilation...

Any suggestions/ideas?

Thanks,
Grieb.

Question: Using Surplus without JSX

Is it possible to use Surplus, but without writing JSX?

This would be analogous to using React with manually written React.createElement() calls. The benefit is being able to transpile from languages that don't support (and can't preserve) JSX.

Feature request: hyphenated function properties

I figured out that I can't do this:

        <Callout slash-color="light" text-color="dark">
            This is some copy text.
        </Callout>

Since I'm used to working with Vue, I figured that either these attributes would be mapped to:
{Callout({ slashColor: "light", textColor: "dark", children: [ "This is some copy text" ] })} OR
{Callout({ "slash-color": "light", "text-color": "dark", children: [ "This is some copy text" ] })}.

Instead, Surplus does neither. It fails parsing because of the hyphens. ๐Ÿ˜ข The main reason I wanted to do this is to reflect attributes in the rendered DOM depending on hyphenated value. But the other reason to do it is to keep the markup consistent. Camelcase attributes look wrong. If I had a preference, I might choose the former, but really either option other than failing parsing would work for me.

SVG attributes from JSX unrecognized

HI Adam again. Last I've mentioned to you, that I'd have to write a small utility to convert svg to jsx, which would then be converted by surplus. So I went and looked, and lo, the React community had already written such a thing. And happily, I went and wrote a little function to translate svg to jsx, then jsx to surplus. However... it doesn't render right! I suspect the reason is because of unrecognized SVG attributes. Let me illustrate.

Let's say I start with this SVG:

<svg viewBox="0 0 100 100">
    <line x1="0" y1="50" x2="100" y2="50" stroke="black" stroke-width="100"></line>
</svg>

It's a pretty idiotic way to make a black square.

Now I run it through JSX:

<svg viewBox="0 0 100 100">
    <line x1={0} y1={50} x2={100} y2={50} stroke="black" strokeWidth={100} />
</svg>

All good. But look again! The stroke-width is now strokeWidth! Turns out (scroll to the bottom) (another link) that SVG attributes are "supported" by JSX. Which seems to mean that the canonical attribute version is camelCased (and thus, basically all svg attributes involving a dash are inflicted). Here is the place where surplus isn't supporting it, and if you run the surplus .compile () (0.5.0-beta11) through this, it becomes:

(function () {
    var __, __line1;
    __ = Surplus.createSvgElement('svg', null, null);
    Surplus.setAttribute(__, "viewBox", "0 0 100 100");
    __line1 = Surplus.createSvgElement('line', null, __);
    Surplus.setAttribute(__line1, "x1", 0);
    Surplus.setAttribute(__line1, "y1", 50);
    Surplus.setAttribute(__line1, "x2", 100);
    Surplus.setAttribute(__line1, "y2", 50);
    Surplus.setAttribute(__line1, "stroke", "black");
    Surplus.setAttribute(__line1, "strokeWidth", 100);
    return __;
})()

Which renders to

<svg viewBox="0 0 100 100">
    <line x1="0" y1="50" x2="100" y2="50" stroke="black" strokeWidth="100"></line>
</svg>

Just a meager line (strokeWidth is not an svg attribute and has no effect).

I'll see if I have time for a PR.

(By the way, turns out the JSX spec seems to say namespaced attributes go with colons... except that the rest of the community and React say they go camelCased.)

Typescript transpiled code support for input statement

Example Typescript code.

import { MdButton } from "md-components/button";

export = (ctrl: any) =>
    <MdButton>Button 1</MdButton>

Outputs to (in typescript 2.3.4 and jsx=preserve settings)

var button_1 = require("md-components/button");
module.exports = function (ctrl) {
    return <button_1.MdButton>Button 1</button_1.MdButton>;
};

The above code is failing: Error: unrecognized content in begin tag at line 3 col 17: ``.MdButton>Button 1</button_1.M''

But using const MdButton = require("md-components/button").MdButton instead of import is working.

Punchlist for 0.5

I'm working on an 0.5 release, but haven't been particularly transparent about what that entails, so this is a feature list and punchlist for what remains on the 0.5 release.

New features.

  • Spread functions {...fn} are now split off to the fn={...} property. {... } is now only for spread properties. I'm not thrilled with the fn= syntax, but it's important to have an explicit split b/w spread objects and functions, and this is the best option I could construct while maintaining JSX syntax. Other opinions welcome. B/c this is a breaking change, I'm bumping the version number to 0.5.
  • SVG support (thanks to @ismail-codar for kicking this off!)
  • Attribute support. JSX properties that are known to be attributes (like aria-*) are set with node.setAttribute() rather than node.property = .... (krausest/js-framework-benchmark#213)
  • Updated plugins for webpack, rollup, browserify and gulp build tools.
  • Smaller components via tuning the Surplus.createElement() function to allow for better minimization.
  • Several fixes to property precedence. The old code sometimes left a stale property around after it had disappeared from a spread, or would let a shadowed property take precedence if it was updated last, etc.
  • Several fixes to corner cases: components with no properties, boolean properties, etc.
  • Compiler now has an actual AST transform infrastructure, with some new transforms and optimizations added.
  • The old '@' syntax if fully removed from the compiler, only JSX remains.

To do:

  • An actual README. Half done now.
  • Merge surplus and surplus-preprocessor packages. They're closely coupled and having different versions of the two would cause several errors.
  • Fix es6 template literals #12
  • property aliases for HTML
  • (maybe) Surplus.content(), for faster all-children updating
  • deep hyphenated props, compiler + .d.ts support (.style-width)
  • tokenizer is stripping '@' chars, left over from old syntax
  • change handling of children for subcomponents to match React: don't always create an array, but rather give undefined -> child -> children[] for 0 -> 1 - > n children
  • the React .d.ts seems to do a better job of typing children. How? And how to copy?

Using styles

Using styles like:

<div style={{ width: '100%' } as CSSStyleDeclaration}>test</div>

throws Uncaught TypeError: Surplus.staticStyle is not a function in runtime.

And this input style.width="50%" screenshot is following in vscode editor.

image
style-width way can be preferred

Bug: "class" attribute on SVG is incorrectly output as "className"

It took me a while for my eyes to spot just why the heck my styles weren't getting applied. Turns out my element starts as:

            <svg
                version="1.1"
                class={ $style.callout__quotation }
                x="0px"
                y="0px"
                viewBox="0 0 48 42.1"
            >
                <polygon points="48,0 48,11.6 39.6,19.5 48,19.5 48,42.1 27.5,42.1 27.5,19.7" />
                <polygon points="20.5,0 20.5,11.6 12,19.5 20.5,19.5 20.5,42.1 0,42.1 0,19.7" />
            </svg>

and ends up as:

<svg version="1.1" className="_callout__quotation_kxemy_95" x="0px" y="0px" viewBox="0 0 48 42.1"><polygon points="48,0 48,11.6 39.6,19.5 48,19.5 48,42.1 27.5,42.1 27.5,19.7"></polygon><polygon points="20.5,0 20.5,11.6 12,19.5 20.5,19.5 20.5,42.1 0,42.1 0,19.7"></polygon></svg>

So, class becomes className, which of course doesn't apply the CSS class.

HTML Entity is escaped in 0.4.3

In the little README.md todo demo the ร— entity shows as &times; in a text node. This also happens in my own Surplus code. My workaround has been to substitute &times; with {String.fromCharCode(255)} etc. so it's pretty low priority.

Browser Support

Hey guys,

Awesome library, just wondering what the browser support is like?
Do Surplus and S require any polyfills for old browsers?

Thanks

Feature request: hide false-y attributes

I had a hidden attribute like hidden={ hideQuotes }. I knew in my demo app that hideQuotes would default to undefined, which I hoped (spoiled by Vue and Polymer, I know) would just remove the hidden attribute.

I tried hidden={ hideQuotes } (casting to boolean), but ended up with hidden="false" which, in HTML, means hidden == true in terms of the property. HTML specifies that boolean attributes should be absent when false, but would be nice if they were also absent when falsey.

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.