crystalline-element
v3.0.1
Published
A collection of Lit enhancements inspired by Stimulus and written in Ruby2JS.
Downloads
15
Readme
❄️ Crystallized
This package has moved to @crystallized/controllers
. Please visit https://github.com/whitefusionhq/crystallized for details.
Lit: Simple. Fast. Web Components.
Crystallized: a collection of Lit 2 enhancements, starting with controllers inspired by Stimulus. Crystallized includes:
DeclarativeActionsController
- lets you add action attributes to elements in the light DOM as a way of providing declarative event handlers.TargetsController
- lets you easily query child nodes in the light DOM using either selectors or explicit attribute-based identifies. Docs coming soon!
Crystallized uses Ruby 3 and Ruby2JS to compile its source code to modern ES6+ JavaScript (example). Crystallized can be used with any modern JS bundler as well as directly in buildless HTML using script type="module"
.
Lit along with Crystallized works great as a spice on top of server-rendered markup originating from backend frameworks like Rails or static sites generators like Bridgetown—providing features not normally found in web component libraries that assume they're only concerned with client-rendered markup and event handling.
You can build an entire suite of reactive frontend components just with Lit/Crystallized, along with a general strategy to enhance your site with a variety of emerging web components and component libraries (Shoelace for example).
NOTE: this package is currently in the process of transitioning to @crystallized/controllers
. All of the below information is deprecated. Please come back shortly for updated instructions!
Installing
yarn add crystalline-element
or
npm i crystalline-element
Using DeclarativeActionsController
It's very simple to add this controller to any Lit 2 component. First let's set up a new test component:
import { LitElement, html } from "lit"
import { DeclarativeActionsController } from "crystalline-element/controllers"
class TestElement extends LitElement {
actions = new DeclarativeActionsController(this)
clickMe() {
this.shadowRoot.querySelector("test-msg").textContent = "clicked!"
}
render() {
return html`
<slot></slot>
<test-msg></test-msg>
`
}
}
customElements.define("test-element", TestElement)
You'll notice that currently nothing actually calls the clickMe
method. Don't worry! We'll declaratively handle that in our regular HTML template:
<test-element>
<article>
<button test-element-action="clickMe">Button</button>
</article>
</test-element>
The tag name of the component (text-element
) plus action
sets up the event handler via an action attribute, with the method name clickMe
being the value of the attribute. This is shorthand for click->clickMe
. The controller defaults to click
if no event type is specified (with a few exceptions, such as submit
for forms and input
or change
for various form controls).
Because DeclarativeActionsController
uses a MutationObserver to keep an eye on HTML in the light DOM, at any time you can update the markup dynamically and actions will work as expected.
In addition, actions don't pass component boundaries. In other words, if you were to add a test-element
inside of another test-element
, the action within the nested test-element
would only call the method for that nested component.
Note: actions are only detected within light DOM and do not traverse shadow trees of child components.
Using CrystallineElement
CrystallineElement is very easy to use. Simply import it, along with helpers from Lit directly, and you can start writing new web components.
More documentation coming soon…
Ruby Example
import [ CrystallineElement, crystallize ], from: "https://cdn.skypack.dev/crystalline-element"
import [ html, css ], from: "https://cdn.skypack.dev/lit"
class MyComponent < CrystallineElement
property :name, String
stylesheet css <<~CSS
p {
font-weight: bold;
}
CSS
define "my-component" # always add below properties, stylesheets, etc.
def render()
html "<p>Hello World! Nice to meet you, #{self.name}</p>"
end
end
class LightDomOnlyComponent < CrystallineElement
define "light-dom-only", shadow_dom: false
# ...
end
localVariable = "functional"
crystallize("functional-component",
properties: {
greeting: { type: String }
}
) do |comp|
html <<~HTML
<p>#{comp.greeting}, you can write "#{localVariable}" components with a handy shorthand!</p>
HTML
end
JavaScript Example
import { CrystallineElement, crystallize } from "https://cdn.skypack.dev/crystalline-element"
import { html, css } from "https://cdn.skypack.dev/lit"
class MyComponent extends CrystallineElement {
static get properties() {
return {
name: { type: String }
}
}
static get styles() {
return css`
p {
font-weight: bold;
}
`
}
render() {
return html`<p>Hello World! Nice to meet you, ${this.name}</p>`
}
}
MyComponent.define("my-component")
class LightDomOnlyComponent extends CrystallineElement {
// ...
}
LightDomComponent.define("light-dom-only", { shadowDom: false })
const localVariable = "functional"
crystallize("functional-component", {
properties: {
greeting: { type: String }
}
}, comp => html`
<p>${comp.greeting}, you can write "${localVariable}" components with a handy shorthand!</p>
`)
Building Source with Ruby2JS
Requires Ruby 3.0. A Ruby version manager like rbenv
is recommended. Run bundle install
to set up the Ruby gems.
Run yarn build
(which gets run by the test
and release
script automatically) to transpile the Ruby src
files to the JS dist
folder.
Testing
Crystalline uses the Modern Web Test Runner and helpers from Open WC for its test suite.
Run yarn test
to run the test suite.
Contributing
- Fork it (https://github.com/whitefusionhq/crystallized/fork)
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
License
MIT