GithubHelp home page GithubHelp logo

devpunks / snuggsi Goto Github PK

View Code? Open in Web Editor NEW
393.0 21.0 17.0 34.81 MB

snuggsi ツ - Easy Custom Elements in ~1kB

Home Page: https://snuggsi.com

License: MIT License

Ruby 0.54% Shell 4.34% JavaScript 78.29% HTML 14.01% TypeScript 0.28% CSS 2.22% WebIDL 0.31% Procfile 0.01%
frontend ecmascript dom es6 template-literals webcomponents custom-elements javascript html

snuggsi's Introduction

snuggsi ツ - Easy Custom Elements in ~1kiloByte

NPM monthly downloads Travis CI build Brotli size npm version dependency status license Pull requests welcome!

All you need is a browser and basic understanding of HTML, CSS, & JavaScript classes to be productive!

Performance is the art of avoiding work - #FreeJewelry 💍 💎

Navigation

  1. You prefer to be D.R.Y. and build reusable web components on a gradual learning curve.

  2. Because You (probably) don't need a JavaScript Framework.

  3. You prefer convention over configuration.

  4. Web Components are ready for production & Custom Elements v1 has support for every modern 🍃 greenfield browser.

Easy Installation

Made with 💖 Vanilla JS™ No need to learn Node.js, React, Webpack, Babel, or Gulp. (You can if ya want to use snuggsiツ with those tools. But you don't need to!)

#UseThePlatform

snuggsiツ works in a plain 'ol HTML file! Simply place the following <script> within your webpage:

<script nomodule src=//unpkg.com/snuggsi></script>

Et Voila (that's it!)

Browser Support

snuggsiツ provides a prolyfill for the following native web platform features:

Support Edge* Chrome* Firefox* Safari 9+ Android* iOS Safari*
Templates
Custom Elements
Slot Replacement

*Indicates the current version of the browser

Quick Tour

snuggsiツ encourages convention over configuration using familiar techniques that are native to all browsers.

Gone are the sleepless nights where your code suffers from <div>itus, or need to install packages on a terminal just to write HTML. People who are more comfortable with HTML should be able to start marking up their ideas immediately! You shouldn't have to know CSS or JavaScript! (But it definitely helps if you need styling and functionality).

snuggsiツ believes in using Progressive Enhancement. Not just with your code, but also with your Developer eXperience (DX). We will start from the beginning with a simple Custom Element and gradually enhance functionality step-by-step.

When picking a name for your custom element there are a few naming conventions you must be aware of. We can simply use hello-world for now.

HTML Declaration

HTML has always been a declarative language. Why not just use it to declare Custom Elements? If you know how to write HTML you can start using snuggsiツ. Sometimes you need to sandbox a section of your page for styling. Other times you need a custom container of complex functionality. Either way you usually start with a plain ole' HTML element declaration:

A Brief History Lesson

Not all HTML tags are created equal!

A "valid HTML Element" has always allowed non-standard tag names (as long as you remember to provide a closing tag). In the bad old days of the web, HTML5 elements were once "non-standard" to HTML 4.0:

<a></a> <!-- valid (hyperlink) -->
<b></b> <!-- valid (HTML4.01)-->
<c><!-- invalid (no closing tag) -->
<c></c> <!-- valid (HTMLUnknownElement) -->
<p></p> <!-- valid (with closing tag)  -->
<p><!-- valid (with optional closing tag)  -->
<img />  <!-- valid (x-HTML self closing tag)  -->
<hr>     <!-- valid (no content tag)  -->

👍 Rule of thumb: Close all non-standard HTML Element tags!


As you learned earlier there are a few conventions to adhere to be considered a "valid Custom Element" you will need an alpha-numeric character followed by a hyphen in the tag name (at minimum):

<foo></foo> <!-- invalid (CUSTOM element but valid HTML element) -->
<foo-bar>   <!-- invalid (no closing tag) -->
<a-></a->   <!-- valid (you only need a character and a hyphen) -->
<foo-bar></foo-bar> <!-- valid -->

👍 Rule of thumb: Use kebab case (with hyphens) for tag names.


We now know enough to be dangerous and make your own Custom Element tag:

<hello-world></hello-world>

Et Voila ツ (No really … That's it!)


At this point your custom element can be styled using CSS just like any other element.

<style>
hello-world {
  background: #bada55
}
</style>

<hello-world>
  Hello
</hello-world>

See A JavaScript-free custom element implementation And Building a <tool-tip> component for more (sans JavaScript) custom elements CSS fun!


Live {token} Declarations

The {token} is simply a well named dynamic variable you will Describe later. {token}s are placeholders which watch for changes to your custom element's class property of the same name. Since they are "placeholders" and not live code, Front-end designers are no longer blocked by needing to install a JavaScript framework just to write CSS!

<foo-bar> This is a token 👉 {baz} and {bat} is another! </foo-bar>

👍 Rule of thumb: If the {token} name is not in a thesaurus then I probably shouldn't use it.


A "live token" is a declarative way to bind data values to your Custom Element. A nice convention to a real historical P.I.T.A. of keeping values updated. Live {token}s are also "✨ automagically" updated each time the element re-renders.

Let's add a {token} to <hello-world>:

<hello-world>

  Hello {planet}

</hello-world>

👍 Rule of thumb: A {token} is not "live" until there is a class description for its functionality.


Lastly, we can visually enhance your <hello-world> Custom Element by making it "block level" with CSS display: block. This way your element plays nicely in your layout:

<hello-world>

  Hello {planet}

  <style>
    hello-world { display: block }
  </style>

</hello-world>

We have finished your Custom Element Declaration using HTML, & CSS!🌟 Now on to your Definition.


Element Definition

Every Custom Element MUST be Defined within the CustomElementsRegistry. This is simple with snuggsiツ

Let's define your element using the Element interface:

// <hello-world> … </hello-world>

Element `hello-world`

👍 Rule of thumb: Use backticks around tag names (``).

This syntax is not JSX. It's actually called tagged template literals and is native to the platform. Custom elements use the native Element interface definition strategy for two reasons:

  1. To prevent you from worrying about browser inconsistencies as the technology matures.
  2. Prevent global namespace pollution. (Element has been native to the web platform for decades!)

Classic JavaScript syntax may also be used. However this should be the job of a transpiler not the developer. Transpilers take care of normalizing Modern JavaScript to a specific retrograde.

Element ('hello-world') // classic javascript definition

class Description

Great so far!🎉 Although your Element behaves like any other HTMLElement, we should add some functionality custom to your needs.

Next, we need to pass a class description to the function returned by your Element definition.

// <hello-world> … </hello-world>

Element `hello-world`

( class HelloWorld extends HTMLElement {  } )

👍 Rule of thumb: MUST define a class which extends HTMLElement

Let's shorten your description up a bit by using an anonymous class expression to describe the class. This convention is preferred as using an explicit class declaration name can potentially pollute the global namespace:

// <hello-world> … </hello-world>

Element `hello-world`

( class extends HTMLElement {  } )

👍 Rule of thumb: Use enclosing parenthesis around (class …) definitions.


Live {token} Definitions

Since we previously declared a {planet} token within your <hello-world> element we need to also define a class property of the same name to replace the {planet} token with a value.

Class properties may look like typical JavaScript Functions. However, they are treated as properties. (called without parenthesis). class properties are described by using the getter and setter annotations before the name.

Let's add a property to your class definition and give {planet} some life:

// <hello-world> … {planet} … </hello-world>

Element `hello-world`

(class extends HTMLElement {

  get planet () // used for {planet} token
    // "✨ automagic" token binding
    { return 'world 🌎' }
})

👍 Rule of thumb: class properties are functions begining with the keywords get & set.

👍 Rule of thumb: {tokens} will use the class property value of the same name by default.

⚠️ The Live {token} value is updated after each re-render but it beyond the scope of this simple example.

Since your hello-world Custom Element is an HTMLElement at its core, we can access your property directly from the DOM!

// <hello-world> … </hello-world>

document.querySelector
  ('hello-world').planet // world 🌎

👍 Rule of thumb: Do not use parenthesis () when calling getters & setters.


Global event Listeners

event handlers can be any method function which can be placed on any child elements and also onto the custom element itself (i.e.onclick=eatBacon). However, you will not have to explicitly set the handler in HTML when you follow native naming conventions. This is the magic behind snuggsiツ Global event Listeners. They register themselves onto the custom element and "listen" for you! As a convenience, your new custom element uses Event Delegation to capture all its children's event bubbles of the same name.

// <hello-world>
//   `onclick` is "automagically" attached
//
//   <a onclick=onsneeze> ACHOO! </a>
// </hello-world>

Element `hello-world`

(class extends HTMLElement {

  // native event handler names
  // are "✨automagically" attached to `hello-world`
  onclick (event) {

    // prevent event from bubbling
    // Custom Element will re-render
    // after event unless prevented
    event.preventDefault ()

    event.target // element which triggered event

    this // is ALWAYS bound to the Custom Element container 🎉
  }

  onsneeze (event) {
    /* must be declared in HTML
      <button onclick=onsneeze>
    */
  }
})

👍 Rule of thumb: Use native GlobalEventHandlers names if you don't want to explicitly set listeners in HTML.

See full list of Global Event Handlers on MDN

Lastly, all event handlers (and global event listeners) are passed a native event object.

P.S. YES the event handler will autobind this to the current custom element instance! 🎉


Hello World! (simple)

Play <hello-world> Demo

…or just copy & 🍝pasta into a new HTML file and have at it!

<!-- 👇 Declaration --------------------------->

<hello-world>

  Hello {planet}

  <button onclick=speak>Greet</button>
</hello-world>


<script src=//unpkg.com/snuggsi></script>
<script>

// 👇 Definition -------------------------------

Element `hello-world`

// 👇 Description ------------------------------

(class extends HTMLElement {

  get planet () // property token
    // "✨ automagic" token binding
    { return 'world 🌎' }

  onclick (e) { // event handler
    // "✨ automagic" event registration
    alert(`You clicked on ${e.target.tagName}`)
  }
  
  speak(e) { // bound event handler
    e.preventDefault()
    alert('One small step...')
  }
})

</script>

Hello Kitty! (advanced)

Play <hello-kitty> Demo

…or just copy & 🍝pasta into a new HTML file and have at it!

<hello-kitty icon=😻 >

  <header>{greeting}</header>

  <figure>
    <figcaption>
      <button onclick=meow>Hello new kitty!</button>
    </figcaption>

    <img alt='Random kitty cat' src={url} onclick=pet >
  </figure>

  <style>
    hello-kitty *
      { margin: 1em; text-align: center }
  </style>

</hello-kitty>

<script src=//unpkg.com/snuggsi></script>
<script>

Element `hello-kitty`

(class extends HTMLElement {

// CONSTRUCTOR ---------------------------------------

  initialize ()
    // see `meow` event handler
    { this.url = 'https://placekitten.com/400/400?image=' }


// PROPERTIES ----------------------------------------

  set icon // on element
    // default to html attribute
    ( value = this.getAttribute `icon` )
      // set html attribute to new value
      { this.setAttribute (`icon`, value) }

  get icon () // from element
    { return this.getAttribute `icon` }

  get greeting () // "✨ automagic" token binding
    { return `<hello-kitty> Carousel ${ this.icon }` }


// METHODS -------------------------------------------

  random () {
    return Math.round
      ( Math.random `` * 16 )
  }


// EVENT HANDLERS ------------------------------------

  onclick (e) {
    // "✨ automagic" global event handler registration
    alert (`You clicked on ${e.target.tagName} ${ this.icon }`)
  }

  pet ()
    { alert `Puuuuuurrrrrrrrrrrr!!!` }

  meow (e) { // custom handler
    e.preventDefault ``

    this.querySelector `img`
      .setAttribute (`src`, this.url + this.random () )

    // element will "✨ automagically" re-render !!!
  }
})

</script>

The <template> is used to define custom element content for use within multiple elements.

Useful when we need to:

  1. Separate a custom element definition into a Web Component
  2. Bind a context to the template using An Array or POJO (Plain Ol' JavaScript Object)
  3. Append rendered template to the document:
    • If context is an object bind a single <template>
    • If context is a collection (i.e. an Array) bind a sequential <template> DocumentFragment per item

<template> With Object Context

<template name=developer>
  <!-- `{nickname}` will bind to `context` property `nickname` -->
  <h1>{nickname}</h1>
</template>

<script src=//unpkg.com/snuggsi></script>
<script>

const
  template = Template `developer`
, context  = { nickname: 'That Beast' }

template.bind (context)

</script>

Rendered Result

<template name="developer">
<!-- invisible
  <h1>{name}</h1>
 -->
</template>

<h1>That Beast</h1><!-- template is used as head for tail insertion -->

<template> With Array of Objects Context

Each <template name> will be mapped over each context item within the array. When the array items are objects each property will map to a respective {token} of the same name.

Note: The # symbol is used to retrieve the collection's current index of iteration.

<ul>
  <template name=item>
    <li>Hello {name}! Your index # is {#}
  </template>
</ul>

<script src=//unpkg.com/snuggsi></script>
<script>

// when context is a collection
const
  template = Template `item`
, context  = [ {name: 'DevPunk'}, {name: 'Snuggsi'} ]

// internal template render for each item in context
template.bind (context)

</script>

Rendered Result

<ul>
  <template name="item">
  <!-- invisible
    <li>Hello {name}! Your index # is {#}
  -->
  </template>

  <li>Hello DevPunk! Your number index # is 0</li>
  <li>Hello Snuggsi! Your number index # is 1</li>
</ul>

<template> With Scalar Array Context

Each <template name> will be mapped over each context item within the array. When the array items are scalar (i.e. strings, numbers, booleans) each item will map to a {self} helper token.

Note: The # symbol is used to retrieve the collection's current index of iteration.

<dl>
  <template name=recipe>
    <dt> Step {#}.
    <dd> {self}.

  </template>
</dl>

<script src=//unpkg.com/snuggsi></script>
<script>

// when context is a collection of scalar variables (i.e. Strings)
const
  template = Template `recipe`
, context  = [
    "Preheat oven"
  , "Place pre-made cake in oven"
  , "Don't burn the cake"
  , "Nom Nom"
  ]

// internal template render for each item in context
template.bind (context)

</script>

Rendered Result

<dl>
  <template name="recipe"></template>

  <dt> Step 1.</dt>
  <dd> Preheat oven.</dd>

  <dt> Step 2.</dt>
  <dd> Place pre-made cake in oven.</dd>

  <dt> Step 3.</dt>
  <dd> Don't burn the cake!</dd>

  <dt> Step 4.</dt>
  <dd> Nom Nom!</dd>

</dl>

Build Process

snuggsiツ is able to use modern compression algorithms to create bundles as small as ~1500 OCTETS (or one 1500byte Ethernet packet frame)

Read More

CalVer (Calendar Versioning)

A chronological CalVer strategy is used instead of SemVer. Read More

Contributors

Contributing while using Visual Studio Code is simple! Read More

snuggsi's People

Contributors

albertopontonio avatar brandondees avatar dependabot[bot] avatar grepsedawk avatar halorium avatar icyc9 avatar mrbernnz avatar olaf-k avatar rianby64 avatar snuggs avatar vicenterd 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

snuggsi's Issues

<js-fiddle> Custom Element

Proposal

<js-fiddle
  id=ewq21w
  theme=dark
  bg=#bada55
  lang=html,css>
</js-fiddle>

Reason

Because keeping up with JSFiddle examples and source code in the repo is like Christmas Kitty

Settings

  • id - We can use GlobalAttributes.id as the chance of there being 2 <js-fiddle>s with the same id on the same page is slim to none. Just doesn't make sense RN. The id attribute will be used as the token within a URL builder inside <js-fiddle> (part of the reason of creating this custom element)

  • language - Not sure if intended usage but we can use GlobalAttributes.lang property to specify html,js,css panes. Here is the spec for lang I kinda wanna (ab)use the attribute for this purpose. Opens up many doors lang=pug :trollface:

  • background color - ????? Dunno the attribute to use for this one but we need it to specify color to compliment theme (see below)

  • theme - Values dark,light for jsFiddle.

Other learnings

@brandondees ya don't know what ya don't know! EUREKA!

I don't know if the following is relevant to the <js-fiddle> component but we DO have a way to POST js-fiddles onto the server. However don't know if there is a mechanism to PUT/PATCH for updates. THIS IS WHAT WE'VE BEEN LOOKING FOR @brandondees @pachonk.

Not sure if the intent is to POST on each request making an ephemeral jsfiddle. The API is a bit unclear. Or I just don't like to read. @tmornini may have some input on WTF is going on with their API.

capture d ecran 2017-07-03 a 10 04 34
capture d ecran 2017-07-03 a 10 04 24

Remove static keyword from the event handler

(class extends HTMLElement {
  
  static onclick () // "automagic" event registration
    { alert (this.textContent) }
})

to be only

(class extends HTMLElement {
  
  onclick () // "automagic" event registration
    { alert (this.textContent) }
})

Docker help

Hello, tom @snuggs referenced me to you.
I am currently in the process of learning docker.
In a current project, I work on I have docker-compose file.
Inside I have set the MySQL root_password as an Environment variable.
Of course this way I can't check in the docker-compose file into git.
So now I am wondering what is the best way to secrets with docker.
I read and found Heroku is using an env. file
Others use the docker secret method which as far I understood works only in a swarm.
So I am confused and looking for someone to shed some light on that.
Thanks in advance

Multiple `{token}`s within same TEXT_NODE only insert last value

PR Diff: - https://github.com/devpunks/snuggsi/pull/60/files

Given the following Custom Element

<foo-bar>{baz} {bat}</foo-bar>

<script>

Element `foo-bar`

(class extends HTMLElement {

  get baz () { return true }
  get bat () { return true }
})

</script>

Would only parse to:

<foo-bar>{baz} true</foo-bar>

This is because since both tokens were within the same TEXT_NODE the second token would set .textContent to default (pre-token replaced) .text. Thus overwriting the previous {token} insertion within the same TEXT_NODE.

Test Case jsFiddle

<hello-world>
  Hello {planet}
</hello-world>

<hello-world>
  {greeting} World
</hello-world>

<hello-world>
  {greeting} {planet}
</hello-world>

<script defer>

// Element Definition -----------------------------

Element `hello-world`

// Class Description ------------------------------

(class extends HTMLElement {

  initialize () { this.append `🌎 ` }

  get greeting () { return 'Bonjour' }
  get planet () { return 'from Mars 👽' }
})

</script>

Before

capture d ecran 2017-06-16 a 20 19 34

After

capture d ecran 2017-06-16 a 20 28 51

show comparison components implemented in common other styles

for a given demo component (pick the most advanced demo, preferably), show the code and live component for each of the most common ways someone is likely to be comparing this tool to. for example if there's an elaborate datepicker widget that supports a variety of different selection modes and configuration options, show the same thing implemented as a react component, as a polymer component, as an angular directive, and with snuggsi. if the code is really cleaner, more understandable, faster, etc. then it should be able to speak for itself.

better "Why?" in the readme

I suggest making WHY? an h2 heading, to make it easier to find and focus on separately from all the content which is irrelevant until someone gets this part

I also think the why section can be re-written to better emphasize what problems it's solving. It can also call out the relevance in terms of WHY NOT X ALTERNATIVE TOOL? I recommend concise bullet lists for these points, hyperlinks for additional explanation optional.

A lot of what's in that section now is really more like a back story for how this project came to be, which is nice to be able to see but it doesn't deliver that instant candy gratification that we're used to getting, so I think getting to the key points up front and center first is worthwhile, before getting into the more long form background explanation.

Named <template> <slot>s not translated during HTMLLinkElement.import

It appears that imported templates are not delegated an initial render call. This leaves the elements within the template to explicitly execute the initial render.

The structures below when executed will display the element in its initial token representation.

  • hello-world.html
<template>
  <hello-world>
   Hello {planet}
  </hello-world>
</template
  
<script src=https://unpkg.com/snuggsi></script>
<script defer>

// Element Definition -----------------------------

Element `hello-world`

// Class Description ------------------------------

(class extends HTMLElement {

  onclick ()
    // "automagic" event registration
    { alert (this.textContent) }

  get planet ()
    // "automagic" token binding
    { return 'world 🌎' }
})

</script>
  • index.html
<script nomodule src=//unpkg.com/snuggsi/snuggsi.min.js></script>
<link rel=import href=hello-world.html>
  • result
    Hello {planet}

Explicit delegation to the render method within the initialize method will render the tokens, as expected. For example:

...
initialize () {
   this.render()
}
...

show live code examples in a demo site

I know you've already started on this. More examples would be good. Fewer clicks to see all the examples and the code that they're based on would be good, too. I'm specifically thinking of how ZURB Foundation demonstrates code snippets alongside various corresponding live implementation examples so you can see what you'll get without having to do anything. They also put it all into one big easy to navigate list that you can scroll through to "window shop" before you decide what to put any effort into using, and everything's also reachable from the nav list on the side in case that's a faster way to get to what you're looking for.

Update gzip compression algorithm

Currently using gzip for compression. @brandondees proposes brotli. I'm all for the experiment.

Helpful script will be
$ npm run compile
This is script makes the sausage in the factory.
https://github.com/devpunks/snuggsi/blob/master/package.json#L48

This runs entire build step and leaves an artifact in the ./dist/snuggsi.min.es location.

Can clobber current gzip script within package.json. Also using duplicate script for weigh script.

https://github.com/devpunks/snuggsi/blob/master/package.json#L56-L60

Consider a modular design of snuggsi

Just something I wanted to bring up, but snuggsi is growing at an amazing rate!
Things like the fileuploader that @VicenteRD is building could be seen as "Out of the original scope of snuggsi", and could add to the overall repository's size when it may be a non-essential piece.
Have you considered going a modular snuggsi approach where you would have a base (let's say around 1kb) then adding literal "plugins" or "modules" to add additional functionality?

Just an idea I had floating around my head a bit the last day or two.

Multiple templates of the same name issue

@snuggs .. I found this issue when using the same name on multiple templates it does not render and it does not let you select the right exchange in the case below ..

<template name=exchange>
  <option>{foo}
</template>
....
Then later
<template name=exchange>
  <option>{foo}
</template>

Current Status of Webcomponents

Slot {token}s Not Binding to `class` After Import

This issue effects <slot> tokens. Does not affect slots with no tokens as children.

Example

Master Document

<link rel=import href=foo-bar.html>

<foo-bar>
  <h1 slot=header>This slot replacement works because it has no token</h1>
</foo-bar>

Imported Document

<template>
  <slot name=header>...</slot>
  <slot name=baz>{bat} shit crazy</slot>
</template>

<script>

Element `foo-bar`

(class extends HTMLElement {

  get bat () { return 'I am' }
})

</script>

Resulting Master Document

<link rel=import href=foo-bar.html>

<foo-bar>
  <h1 slot=header>This slot replacement works because it has no token</h1>

  <!-- `bat` never binds after import -->
  <slot name=baz>{bat} shit crazy</slot>
</foo-bar>

Expected Master Document

<foo-bar>
  <h1 slot=header>This slot replacement works because it has no token</h1>

  <!-- `bat` binds 'I am' from computed property -->
  <slot name=baz>I am shit crazy</slot>
</foo-bar>

JSFiddle doesn't load HTML Imports

@brandondees @Robertchristopher For some reason the <link rel=import> doesn't work on JSFiddle but does work from our examples.

Do keep in mind JSFiddle evaluates it's REPL using an <ifame> perhaps some content policy issues?
A mystery to me ❓ ❓ ❓

Have also tweeted to GOOGLE Chrome Dev team.
https://twitter.com/snuggsi/status/868884622683328513

Web Component

JSFiddle example - https://jsfiddle.net/xqycvrpL

capture d ecran 2017-05-28 a 13 32 17

Assigned Slot Usage

<user-list> ReactiveJS sample (still) not working

https://github.com/devpunks/snuggsi/tree/master/examples/user-list
https://github.com/devpunks/snuggsi/blob/master/examples/user-list/reactive.es

@Robertchristopher i've carried the football as far as i could with this. The plus side is we were able to remove 60% of the code and get down to a single Mixin.

(See mixin documetation here)
Class Mixin documentation

Need some help on crossing the finish line with this one. A GREAT example and learned a ton.

Here are the references I've been using:

Help wanted: clear explanation of how ES class declarations work scoping-wise.

I'm missing some documentation / instruction on how to use functions defined in ES class declarations from within other functions defined in the same class declaration.

I've experimented with various combinations of this.func() get func() func() this.func etc. and while the properties render into the element's template contents as expected, I can't get the same value into another function context for use in behavior. I'd like some help finding or creating some clear and comprehensive documentation about how this works exactly, and what the standard/best practices are for it.

demonstrate the awesome fluid typography using css vars, but with the whole toolchain for solid cross-browser support

I'm working on a project and using some custom css properties / vars based on some stuff @snuggs showed me, but now that i've plugged it into webpacker's default postcss pipeline, the css custom properties are not behaving the same as they do natively. the values are being calculated at compile time and substituted instead of computed dynamically like the real vars will do on the browser, so this means unless I jump through a bunch of hoops to make everything play nice with that expectation, I'm going to have dramatically inconsistent scaling if i switch between browser-support transpilation and native. If we had a PostCSS plugin that provides a best of both worlds approach by keeping the native --vars in the output but providing a "less pretty but works" fallback rule for non-compliant browsers, I could probably be happy with that. Right now I think I may either have to encode those rules and fallbacks by hand and bypass transpilation (annoying because that means I lose other productive features) or I'll code everything to make the non-native plugin output render as intended, and then have to re-do all the same styles when the time comes to remove the transpilation /polyfill step.

I'm also looking for solid patterns for organizing an entire project's stylesheets and media queries, since I'm currently running into some friction on the way I thought would be nice.

Browsers I'm targeting are all latest/supported versions of Chrome, Safari, FF, Edge, IE11.

move readme documentation 'further reading' links to a 'further reading' subheading section

README is progressing but is turning into a big flat wall of text pretty quickly, hard to parse quickly by scanning through to find what you want. establishing some conventional structures that can be repeated across subsections can make those sections easier to grok while skimming through. specifically I'm noticing that almost every topical section has a series of links to things like specification documents and tutorials, usually near the end. why not group those into a tidy little list under a "Further Reading" subheading for each topic, so that it's easy to filter that out if you're aiming to focus on the TL;DR bits, or easy to focus on if you're really looking to dig into a topic.

The onconnect event handler is not triggered when defined through element

It appears that the onconnect event handler is not triggered when defined through an element.

<script nomodule src=https://unpkg.com/snuggsi></script>
  <input-output onconnect=connectComponent>
    <input id=input-output placeholder="Hello">
    <output for=input-output>{value}</output>
  </input-output>

<script nomodule async defer>
    Element `input-output`
    (class extends HTMLElement {
      static oninput ()
      {
        this.render() 
      }

      static connectComponent () {
        alert("Connecting ....")
      }

      get value () 
      {
        return this.select ('input').value || 'enter something' 
      }
    })
</script>

Automate render after event handler

Default

currently, we call always render

static onnext () {
  this.date_from_month (+1)
  this.render () 
}

We can implicitly render on all event handlers.

static onnext () {
  this.date_from_month (+1)
  // this.render () // automagically gets rendered.
}

Preventing Default

Inject the event object into the handler
MDN Event Object

static onclick (event) {
  event.preventDefault () // stops render

  alert (this.textContent)

  this.render () // explicitly render component
}

show code snippet alongside live demo examples

if we're gonna demonstrate how to use the code to create cool UI bits, it's ideal to show the code and the testable demo right next to each other so it's easy to see what input relates to what output without navigating around between pages or manually experimenting with code to try to replicate something.

show speed comparison with react and other top alternatives side by side

benchmark numbers are fine, but it might also be fun to have a demo page which loads two implementations of similar functionality side by side in parallel iframes, to race them against each other. the demo could do something inherently slow like iterating data mutations through a long list and re-rendering nested elements based on it.

Multiple instances of a custom element only render last instance.

@brandondees I think this is from the import/clone phase of the element birth. We override <link rel=import>.onimport event on each instance of the custom element so the last element is the one that actually triggers the event. This is the nature of on events as they traditionally can only have one event handler attached to them. Great for defaults as in our on* event conventions. But horrible when you want to trigger n events. Will revert to using an addEventListener strategy as this is how to handle multiple events from the same <link rel=import> makes total sense as there will only be ONE <link rel=import> for n clones.

EventTarget.connectedCallback (triggered each instance) Where 🐛 bug is. Only the last .onimport gets handled. as previous get overriden -> https://github.com/devpunks/snuggsi/blob/master/elements/event-target.es#L27-L31

Element.onimport routine -> https://github.com/devpunks/snuggsi/blob/master/elements/component.es#L42-L62

Why/when to use EventTarget.addEventListener -> https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Notes

HTMLLinkElement.onimport event -> https://github.com/devpunks/snuggsi/blob/master/elements/html-link-element.es#L15-L37
Hopefully we won't have to upstream this to webcomponents.js polyfill. We rely heavily on their "When imports ready" event.

capture d ecran 2017-05-19 a 16 45 56

Object definitions for interpolated css styling

As of now, declaring functions that return styles to be interpolated into elements requires imperative string concatenation.

I propose the ability to define an object mapping of styles.

Example:

<input style={input_style}>
get input_style () {
  return {
    'visibility': 'hidden',
    ...
  }
}

A naive implementation would be interpreting objects returned from tokens within the style tag and resolving them to their css representation. An example abstraction for this is shown below.

const

  createStyle = (params) => {
    return Object.keys(params)
      .filter((i) => params[i])
      .map((param) => {
        return `${param}=${params[param]}`
      }).join(';')
  }

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.