mirror of
https://github.com/bigskysoftware/htmx.git
synced 2026-03-13 18:08:10 +00:00
update hyperscript and docs
This commit is contained in:
parent
06dc449e91
commit
84200f4599
@ -980,7 +980,7 @@ return (function () {
|
||||
}
|
||||
if (detail.error) {
|
||||
logError(detail.error);
|
||||
triggerEvent(elt, "error.htmx", {errorDetail:detail})
|
||||
triggerEvent(elt, "error.htmx", {errorInfo:detail})
|
||||
}
|
||||
var eventResult = elt.dispatchEvent(event);
|
||||
withExtensions(elt, function (extension) {
|
||||
|
||||
@ -78,6 +78,9 @@
|
||||
|
||||
function makeTokensObject(tokens, consumed, source) {
|
||||
|
||||
var ignoreWhiteSpace = true;
|
||||
matchTokenType("WHITESPACE"); // consume any initial whitespace
|
||||
|
||||
function raiseError(tokens, error) {
|
||||
_parser.raiseParseError(tokens, error);
|
||||
}
|
||||
@ -141,9 +144,22 @@
|
||||
function consumeToken() {
|
||||
var match = tokens.shift();
|
||||
consumed.push(match);
|
||||
if(ignoreWhiteSpace) {
|
||||
matchTokenType("WHITESPACE"); // consume any whitespace until the next token
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
function consumeUntilWhitespace() {
|
||||
var tokenList = [];
|
||||
ignoreWhiteSpace = false;
|
||||
while (currentToken() && currentToken().type !== "WHITESPACE") {
|
||||
tokenList.push(consumeToken());
|
||||
}
|
||||
ignoreWhiteSpace = true;
|
||||
return tokenList;
|
||||
}
|
||||
|
||||
function hasMore() {
|
||||
return tokens.length > 0;
|
||||
}
|
||||
@ -164,7 +180,8 @@
|
||||
list: tokens,
|
||||
source: source,
|
||||
hasMore: hasMore,
|
||||
currentToken: currentToken
|
||||
currentToken: currentToken,
|
||||
consumeUntilWhitespace: consumeUntilWhitespace
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,11 +194,12 @@
|
||||
var lastToken = "<START>";
|
||||
|
||||
while (position < source.length) {
|
||||
consumeWhitespace();
|
||||
if (currentChar() === "-" && nextChar() === "-") {
|
||||
consumeComment();
|
||||
} else {
|
||||
if (!possiblePrecedingSymbol() && currentChar() === "." && isAlpha(nextChar())) {
|
||||
if (isWhitespace(currentChar())) {
|
||||
tokens.push(consumeWhitespace());
|
||||
} else if (!possiblePrecedingSymbol() && currentChar() === "." && isAlpha(nextChar())) {
|
||||
tokens.push(consumeClassReference());
|
||||
} else if (!possiblePrecedingSymbol() && currentChar() === "#" && isAlpha(nextChar())) {
|
||||
tokens.push(consumeIdReference());
|
||||
@ -281,7 +299,7 @@
|
||||
function consumeOp() {
|
||||
var value = consumeChar(); // consume leading char
|
||||
while (currentChar() && OP_TABLE[value + currentChar()]) {
|
||||
value += consumeChar();
|
||||
value += consumeChar();
|
||||
}
|
||||
var op = makeOpToken(OP_TABLE[value], value);
|
||||
op.value = value;
|
||||
@ -329,13 +347,18 @@
|
||||
}
|
||||
|
||||
function consumeWhitespace() {
|
||||
var whitespace = makeToken("WHITESPACE");
|
||||
var value = "";
|
||||
while (currentChar() && isWhitespace(currentChar())) {
|
||||
if (isNewline(currentChar())) {
|
||||
column = 0;
|
||||
line++;
|
||||
}
|
||||
consumeChar();
|
||||
value += consumeChar();
|
||||
}
|
||||
whitespace.value = value;
|
||||
whitespace.end = position;
|
||||
return whitespace;
|
||||
}
|
||||
}
|
||||
|
||||
@ -458,6 +481,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
function evalTarget(root, path) {
|
||||
if (root.length) {
|
||||
var last = root;
|
||||
} else {
|
||||
var last = [root];
|
||||
}
|
||||
|
||||
while (path.length > 0) {
|
||||
var prop = path.shift();
|
||||
var next = []
|
||||
// flat map
|
||||
for (var i = 0; i < last.length; i++) {
|
||||
var element = last[i];
|
||||
var nextVal = element[prop];
|
||||
if (nextVal && nextVal.length) {
|
||||
next = next.concat(nextVal);
|
||||
} else {
|
||||
next.push(nextVal);
|
||||
}
|
||||
}
|
||||
last = next;
|
||||
}
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
function getScript(elt) {
|
||||
for (var i = 0; i < SCRIPT_ATTRIBUTES.length; i++) {
|
||||
var scriptAttribute = SCRIPT_ATTRIBUTES[i];
|
||||
@ -499,7 +548,7 @@
|
||||
var ctx = ctxArg;
|
||||
} else {
|
||||
var src = typeOrSrc;
|
||||
var ctx = {};
|
||||
var ctx = {};
|
||||
var type = "expression";
|
||||
}
|
||||
ctx = ctx || {};
|
||||
@ -552,6 +601,7 @@
|
||||
return {
|
||||
typeCheck: typeCheck,
|
||||
forEach: forEach,
|
||||
evalTarget: evalTarget,
|
||||
triggerEvent: triggerEvent,
|
||||
matchesSelector: matchesSelector,
|
||||
getScript: getScript,
|
||||
@ -599,6 +649,20 @@
|
||||
}
|
||||
})
|
||||
|
||||
_parser.addGrammarElement("nakedString", function (parser, tokens) {
|
||||
if (tokens.hasMore()) {
|
||||
var tokenArr = tokens.consumeUntilWhitespace();
|
||||
tokens.matchTokenType("WHITESPACE");
|
||||
return {
|
||||
type: "nakedString",
|
||||
tokens: tokenArr,
|
||||
transpile: function () {
|
||||
return "'" + tokenArr.map(function(t){return t.value}).join("") + "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
_parser.addGrammarElement("number", function (parser, tokens) {
|
||||
var number = tokens.matchTokenType('NUMBER');
|
||||
if (number) {
|
||||
@ -1019,21 +1083,24 @@
|
||||
});
|
||||
|
||||
_parser.addGrammarElement("target", function (parser, tokens) {
|
||||
var value = parser.parseAnyOf(["symbol", "classRef", "idRef"], tokens);
|
||||
if (value == null) {
|
||||
var root = parser.parseAnyOf(["symbol", "classRef", "idRef"], tokens);
|
||||
if (root == null) {
|
||||
parser.raiseParseError(tokens, "Expected a valid target expression");
|
||||
}
|
||||
|
||||
var propPath = []
|
||||
while (tokens.matchOpToken(".")) {
|
||||
propPath.push(tokens.requireTokenType("IDENTIFIER").value)
|
||||
}
|
||||
|
||||
return {
|
||||
type: "target",
|
||||
value: value,
|
||||
transpile: function (context) {
|
||||
if (value.type === "classRef") {
|
||||
return parser.transpile(value);
|
||||
} else if (value.type === "idRef") {
|
||||
return "[" + parser.transpile(value) + "]";
|
||||
} else {
|
||||
return "[" + parser.transpile(value) + "]"; //TODO, check if array?
|
||||
}
|
||||
propPath: propPath,
|
||||
root: root,
|
||||
transpile: function () {
|
||||
return "_hyperscript.runtime.evalTarget(" + parser.transpile(root) + ", [" + propPath.map(function (prop) {
|
||||
return "\"" + prop + "\""
|
||||
}).join(", ") + "])";
|
||||
}
|
||||
};
|
||||
});
|
||||
@ -1091,6 +1158,15 @@
|
||||
} else {
|
||||
var from = parser.parseElement("implicitMeTarget", tokens);
|
||||
}
|
||||
|
||||
var args = [];
|
||||
if (tokens.matchOpToken("(")) {
|
||||
do {
|
||||
args.push(tokens.requireTokenType('IDENTIFIER'));
|
||||
} while (tokens.matchOpToken(","))
|
||||
tokens.requireOpToken(')')
|
||||
}
|
||||
|
||||
var start = parser.parseElement("commandList", tokens);
|
||||
var eventListener = {
|
||||
type: "eventListener",
|
||||
@ -1102,6 +1178,7 @@
|
||||
"var my = me;\n" +
|
||||
"_hyperscript.runtime.forEach( " + parser.transpile(from) + ", function(target){\n" +
|
||||
" target.addEventListener('" + parser.transpile(on) + "', function(event){\n" +
|
||||
args.map(function(arg){return "var " + arg.value + " = event.detail." + arg.value + ";"}).join("\n") + "\n" +
|
||||
parser.transpile(start) +
|
||||
" })\n" +
|
||||
"})\n" +
|
||||
@ -1301,14 +1378,24 @@
|
||||
} else {
|
||||
var from = parser.parseElement("implicitAllTarget")
|
||||
}
|
||||
|
||||
if (tokens.matchToken("for")) {
|
||||
var forElt = parser.parseElement("target", tokens);
|
||||
} else {
|
||||
var forElt = parser.parseElement("implicitMeTarget")
|
||||
}
|
||||
|
||||
return {
|
||||
type: "takeCmd",
|
||||
classRef: classRef,
|
||||
from: from,
|
||||
forElt: forElt,
|
||||
transpile: function () {
|
||||
var clazz = this.classRef.value.substr(1);
|
||||
return " _hyperscript.runtime.forEach(" + parser.transpile(from) + ", function (target) { target.classList.remove('" + clazz + "') }); " +
|
||||
"me.classList.add('" + clazz + "');";
|
||||
"_hyperscript.runtime.forEach( " + parser.transpile(forElt) + ", function (target) {" +
|
||||
" target.classList.add('" + clazz + "')" +
|
||||
"})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1369,13 +1456,9 @@
|
||||
parser.raiseParseError(tokens, "Expected one of 'into', 'before', 'afterbegin', 'beforeend', 'after'")
|
||||
}
|
||||
var target = parser.parseElement("target", tokens);
|
||||
var propPath = []
|
||||
while (tokens.matchOpToken(".")) {
|
||||
propPath.push(tokens.requireTokenType("IDENTIFIER").value)
|
||||
}
|
||||
|
||||
var directWrite = propPath.length === 0 && operation.value === "into";
|
||||
var symbolWrite = directWrite && target.value.type === "symbol";
|
||||
var directWrite = target.propPath.length === 0 && operation.value === "into";
|
||||
var symbolWrite = directWrite && target.root.type === "symbol";
|
||||
if (directWrite && !symbolWrite) {
|
||||
parser.raiseParseError(tokens, "Can only put directly into symbols, not references")
|
||||
}
|
||||
@ -1383,34 +1466,33 @@
|
||||
return {
|
||||
type: "putCmd",
|
||||
target: target,
|
||||
propPath: propPath,
|
||||
op: operation.value,
|
||||
symbolWrite: symbolWrite,
|
||||
value: value,
|
||||
transpile: function () {
|
||||
if (this.symbolWrite) {
|
||||
return "var " + target.value.name + " = " + parser.transpile(value);
|
||||
return "var " + target.root.name + " = " + parser.transpile(value);
|
||||
} else {
|
||||
var dotPath = propPath.length === 0 ? "" : "." + propPath.join(".");
|
||||
if (this.op === "into") {
|
||||
var lastProperty = target.propPath.pop(); // steal last property for assignment
|
||||
return "_hyperscript.runtime.forEach( " + parser.transpile(target) + ", function (target) {" +
|
||||
" target" + dotPath + "=" + parser.transpile(value) +
|
||||
" target." + lastProperty + "=" + parser.transpile(value) +
|
||||
"})";
|
||||
} else if (this.op === "before") {
|
||||
return "_hyperscript.runtime.forEach( " + parser.transpile(target) + ", function (target) {" +
|
||||
" target" + dotPath + ".insertAdjacentHTML('beforebegin', " + parser.transpile(value) + ")" +
|
||||
" target.insertAdjacentHTML('beforebegin', " + parser.transpile(value) + ")" +
|
||||
"})";
|
||||
} else if (this.op === "afterbegin") {
|
||||
return "_hyperscript.runtime.forEach( " + parser.transpile(target) + ", function (target) {" +
|
||||
" target" + dotPath + ".insertAdjacentHTML('afterbegin', " + parser.transpile(value) + ")" +
|
||||
" target.insertAdjacentHTML('afterbegin', " + parser.transpile(value) + ")" +
|
||||
"})";
|
||||
} else if (this.op === "beforeend") {
|
||||
return "_hyperscript.runtime.forEach( " + parser.transpile(target) + ", function (target) {" +
|
||||
" target" + dotPath + ".insertAdjacentHTML('beforeend', " + parser.transpile(value) + ")" +
|
||||
" target.insertAdjacentHTML('beforeend', " + parser.transpile(value) + ")" +
|
||||
"})";
|
||||
} else if (this.op === "after") {
|
||||
return "_hyperscript.runtime.forEach( " + parser.transpile(target) + ", function (target) {" +
|
||||
" target" + dotPath + ".insertAdjacentHTML('afterend', " + parser.transpile(value) + ")" +
|
||||
" target.insertAdjacentHTML('afterend', " + parser.transpile(value) + ")" +
|
||||
"})";
|
||||
}
|
||||
}
|
||||
@ -1452,20 +1534,17 @@
|
||||
if (method.value === "GET") {
|
||||
tokens.requireToken("from");
|
||||
} else {
|
||||
tokens.requireToken("to");
|
||||
if (!tokens.matchToken("to")) {
|
||||
var data = parser.parseElement("expression", tokens);
|
||||
tokens.requireToken("to");
|
||||
}
|
||||
}
|
||||
|
||||
var url = parser.parseElement("string", tokens);
|
||||
if (url == null) {
|
||||
parser.raiseParseError(tokens, "Requires a URL");
|
||||
}
|
||||
if (tokens.matchToken("with")) {
|
||||
tokens.requireToken("body");
|
||||
var data = parser.parseElement("expression", tokens);
|
||||
if (data == null) {
|
||||
parser.raiseParseError(tokens, "Requires a URL");
|
||||
}
|
||||
var url = parser.parseElement("nakedString", tokens);
|
||||
}
|
||||
|
||||
return {
|
||||
type: "requestCommand",
|
||||
method: method,
|
||||
@ -1473,8 +1552,8 @@
|
||||
var capturedNext = this.next;
|
||||
delete this.next;
|
||||
return "_hyperscript.runtime.ajax('" + method.value + "', " +
|
||||
parser.transpile(url) + ", " +
|
||||
"function(it){ " + parser.transpile(capturedNext) + " }," +
|
||||
parser.transpile(url) + ", " +
|
||||
"function(response){ " + parser.transpile(capturedNext) + " }," +
|
||||
parser.transpile(data, "null") + ")";
|
||||
}
|
||||
};
|
||||
|
||||
56
www/docs.md
56
www/docs.md
@ -581,42 +581,70 @@ if you want to log everything while developing.
|
||||
|
||||
**NOTE: hyperscript is in very early alpha**
|
||||
|
||||
Htmx has a sister project named [hyperscript](https://hyperscript.org). Hyperscript is a small scripting language
|
||||
designed to be expressive, making it ideal for embedding directly in HTML, handling custom events, etc. The language
|
||||
is inspired by [HyperTalk](http://hypercard.org/HyperTalk%20Reference%202.4.pdf), javascript, [gosu](https://gosu-lang.github.io/)
|
||||
and others.
|
||||
Hyperscript is a small scripting language designed to be expressive, making it ideal for embedding directly in HTML,
|
||||
handling custom events, etc. The language is inspired by [HyperTalk](http://hypercard.org/HyperTalk%20Reference%202.4.pdf),
|
||||
javascript, [gosu](https://gosu-lang.github.io/) and others.
|
||||
|
||||
The language can be explored more fully on its website:
|
||||
You can explore the language more fully on its main website:
|
||||
|
||||
<http://hyperscript.org>
|
||||
|
||||
### Catching Events with Hyperscript
|
||||
### Events & Hyperscript
|
||||
|
||||
A primary use case of hyperscript is catching and responding to events triggered by htmx.
|
||||
Hyperscript was designed to help address features and functionality from intercooler.js that are not implemented in htmx
|
||||
directly, in a more flexible and open manner. One of its prime features is the ability to respond to arbitrary events
|
||||
on a DOM element, using the `on` syntax:
|
||||
|
||||
Here is an example with an element that is shown for 5 seconds, then removes the element:
|
||||
```html
|
||||
<div _="on afterSettle.htmx log 'Settled!'">
|
||||
...
|
||||
</div>
|
||||
```
|
||||
|
||||
This will log `Settled!` to the console when the `afterSettle.htmx` event is triggered.
|
||||
|
||||
#### intercooler.js features & hyperscript implementations
|
||||
|
||||
Below are some examples of intercooler features and the hyperscript equivalent.
|
||||
|
||||
##### `ic-remove-after`
|
||||
|
||||
Intercooler provided the [`ic-remove-after`](http://intercoolerjs.org/attributes/ic-remove-after.html) attribute
|
||||
for removing an element after a given amount of time.
|
||||
|
||||
In hyperscript this is implemented like so:
|
||||
|
||||
```html
|
||||
<div _="on load wait 5s then remove me">Here is a temporary message!</div>
|
||||
```
|
||||
|
||||
Here is an example that posts all htmx errors to a server URL:
|
||||
##### `ic-post-errors-to`
|
||||
|
||||
Intercooler provided the [`ic-post-errors-to`](http://intercoolerjs.org/attributes/ic-post-errors-to.html) attribute
|
||||
for posting errors that occured during requests and responses.
|
||||
|
||||
In hyperscript similar functionality is implemented like so:
|
||||
|
||||
```html
|
||||
<body _="on error.htmx ajax POST to /errors with body event.detail.errorDetail">
|
||||
<body _="on error.htmx(errorInfo) ajax POST errorInfo to /errors">
|
||||
...
|
||||
</body>
|
||||
```
|
||||
|
||||
Here is an example that takes an `active` class from other tabs:
|
||||
##### `ic-switch-class`
|
||||
|
||||
Intercooler provided the [`ic-switch-class`](http://intercoolerjs.org/attributes/ic-switch-class.html) attribute, which
|
||||
let you switch a class between siblings.
|
||||
|
||||
In hyperscript you can implement similar functionality like so:
|
||||
|
||||
```html
|
||||
<div hx-target="#tabBody" _="on beforeOnLoad.htmx take .active from .tabs for event.target">
|
||||
<a class="tabs" hx-get="/tabl1" >Tab 1</a>
|
||||
<div hx-target="#content" _="on beforeOnLoad.htmx take .active from .tabs for event.target">
|
||||
<a class="tabs active" hx-get="/tabl1" >Tab 1</a>
|
||||
<a class="tabs" hx-get="/tabl2">Tab 2</a>
|
||||
<a class="tabs" hx-get="/tabl3">Tab 3</a>
|
||||
</div>
|
||||
<div id="tabBody">...</div>
|
||||
<div id="content">Tab 1 Content</div>
|
||||
```
|
||||
|
||||
## <a name="config"></a>[Configuring htmx](#config)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user