npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ccnq-ko

v3.1.0

Published

CCNQ Knockout Widget toolbox

Downloads

8

Readme

Usage

In the main application:

ko = require 'knockout'
{Widget,widget} = (require 'ccnq-ko-widget') ko
html = ->
  widget 'foo'
ko.applyBindings foo: new Widget (...)

In the widget:

module.exports = (require 'ccnq-ko').widget 'widget-tag', ->

  # data-model/constructor
  @data (value) -> @field = @ko.observable value
  # @constructor is a synonim for @data

  @view ({$root,data}) ->
  # any behavior
  # the fields of the constructor are already injected
  # the data object itself is available as {data}, {value} and {doc}

  @html ({div}) ->   # teacup+databind as parameter

Widget creation

The name provided is expected to be the HTML tag, it should be dash-separated: my-widget

widget = (tag_name,f) ->

The class name is: MyWidget

  re = /(^.|-.)/gi
  class_name = tag_name.replace re, (x) -> (x.substr x.length-1).toUpperCase()

The Teacup tag name: my_widget

  re = /-/g
  widget_name = tag_name.replace re, '_'

The function provided to widget is called with a small DSL:

  ctx =
    tag_name: tag_name
    class_name: class_name
    widget_name: widget_name

  ctx._tag = (f) ->
    {tag} = teacup
    tag tag_name, params: "value:#{f},$root:$root"
  • data is the constructor for the data model; ideally it should work with ko.toJS

    ctx.data = ctx.constructor = (data) ->
      ctx._data = data
  • view is the view model (behavior); the root of this is already populated with copies of the content of the object set by data

    ctx.view = (view) ->
      ctx._view = view
  • html is the content as a Teacup template; teacup+databind is provided as parameter

    ctx.html = (html) ->
      ctx._html = html

The widget module returns a function that must be called with the instance of Knockout

  (ko) ->

    init ko

    ctx.ko = ko
    f.call ctx, ko

If no data is provided the default behavior is to inject observables. FIXME: Replace with ko.mapping?

    ctx._data ?= class DefaultData
      constructor: (value) ->
        if typeof value is 'object'
          for own k,v of value
            this[k] = ko.observable v
        else
          @value = ko.observable value

If no view is provided the default behavior is to do nothing.

    ctx._view ?= ->

If no html is provided the default behavior is to output a comment that it wasn't provided.

    ctx._html ?= -> "<!-- No html was provided in #{tag_name} -->"

    if ko.components.isRegistered tag_name
      console.log "Not registering #{tag_name} twice."
    else
      ko.components.register tag_name,
        viewModel: ({value,$root}) ->
          for own k,v of value
            this[k] = v
          ctx._view.call this, {value,$root,ko,data:value,doc:value}
        template: teacup: ctx._html

    res = {tag_name,widget_name,class_name}

and returns a new Teacup widget

    res[widget_name] = ctx._tag

and the data-model constructor

    res[class_name] = ctx._data
    res

Loaders for Knockout

init = (ko) ->
  return if ko.__ccnq_init
  ko.__ccnq_init = true

  loader =

This allows us to dynamically render Teacup template. This solves for example problems with non-unique names for radio-buttons across multiple renderings.

    loadTemplate: (name, config, next) ->
      if config.teacup?
        html = teacup.render config.teacup, teacup
        ko.components.defaultLoader.loadTemplate name, html, next
      else
        next null

  ###
    loadViewModel: (name, config, next) ->
      if config.special
        constructor = ->
          config.special.apply this, arguments
        ko.components.defaultLoader.loadViewModel name, constructor, next
  ###

  ko.components.loaders.unshift loader

teacup = require 'teacup'
teacup.use (require 'teacup-databind')()

module.exports = widget
module.exports.widget = widget