diff --git a/cspell.json b/cspell.json index f873a11e..59fd3eb1 100644 --- a/cspell.json +++ b/cspell.json @@ -4,20 +4,26 @@ "words": [ "afterbegin", "afterend", + "autofocus", "beforebegin", "beforeend", "beforeunload", + "beresp", + "browsersl", + "browserslist", "Brrrrr", "contenteditable", "dblclick", "Delisle", "desugar", + "desugared", "desugars", "Fastly", "figcaption", "fortawesome", "hastscript", "importmap", + "Jetpack", "jridgewell", "keymap", "labelledby", @@ -32,6 +38,7 @@ "onbeforeinput", "openjsf", "optgroup", + "popovertarget", "renderable", "reorder", "reorderer", @@ -43,15 +50,9 @@ "tablist", "taglib", "touchstart", - "webp", - "WHATWG", - "beresp", "TTFB", - "Jetpack", - "popovertarget", - "autofocus", - "browserslist", - "browsersl" + "webp", + "WHATWG" ], "ignoreRegExpList": [], "files": ["*", "docs/**/*", "src/**/*"], diff --git a/docs/explanation/controllable-components.md b/docs/explanation/controllable-components.md index 7dd98f83..96ac6dc4 100644 --- a/docs/explanation/controllable-components.md +++ b/docs/explanation/controllable-components.md @@ -146,7 +146,7 @@ Because this is a common pattern, Marko provides a [binding shorthand](../refere ``` > [!NOTE] -> The [binding shorthand](../reference/language.md#shorthand-change-handlers-two-way-binding) acts differently when used with an _identifier_ versus a _member expression_. Above is the identifier behavior; we'll see the member expression behavior next. +> The [binding shorthand](../reference/language.md#shorthand-change-handlers-two-way-binding) acts differently when used with an _identifier_ versus a _member expression_. ### Controllable `` @@ -209,6 +209,36 @@ export interface Input { ${count} ``` +### Refining Functions + +The shorthand may include a [refining function](../reference/language.md#refining-function) that transforms the value before assignment. This is useful when the child component receives a broad type (e.g. native tag attributes may receive `number | string`) but the parent requires something more narrow. + +```marko + + + +``` + +This example desugars to include `parseFloat` in its setter. + +```marko + + + +``` + +This shorthand takes _any_ function, including custom ones. + +```marko + + + + +static function uppercase(str: string) { + return str.toUpperCase() +} +``` + ## More Power The controllable pattern allows the _user_ of a component to decide whether to manage state. Simple cases remain simple, but complex state management is also possible. diff --git a/docs/reference/language.md b/docs/reference/language.md index 78741e39..18d74903 100644 --- a/docs/reference/language.md +++ b/docs/reference/language.md @@ -286,6 +286,28 @@ For [Property Accessors](https://developer.mozilla.org/en-US/docs/Web/JavaScript ``` +#### Refining function + +The [change handler shorthand](#shorthand-change-handlers-two-way-binding) may optionally include a function that transforms the value before assignment. The function name appears between colons (`:refiningFunction:=`). + +```marko + + +// desugars to + + +``` + +For [Property Accessors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors), the desugared handler includes a boolean expression. + +```marko + + +// desugars to + + { input.numChange(parseFloat(newValue)) })> +``` + ### Shorthand `class` and `id` [Emmet style](https://docs.emmet.io/abbreviations/syntax/#id-and-class) `class` and `id` attribute shorthands are supported. diff --git a/docs/tutorial/components-and-reactivity.md b/docs/tutorial/components-and-reactivity.md index a31e3764..ec85cd73 100644 --- a/docs/tutorial/components-and-reactivity.md +++ b/docs/tutorial/components-and-reactivity.md @@ -27,9 +27,10 @@ Of course, right now we aren't keeping track of the value that this input contai ## Syncing State -Now the `` has an initial value, but we still aren't keeping track of it when it changes. One way to do this is by listening for [the `input` event](https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event) with an [event handler](../reference/native-tag.md#event-handlers): +Now the `` has an initial value, but we still aren't keeping track of it when it changes. One way you may think to do this is by listening for [the `input` event](https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event) with an [event handler](../reference/native-tag.md#event-handlers): ```marko +// Warning: There's a better way to do this! ` has an initial value, but we still aren't keeping track of it
It's ${degF}°F
``` -Aha! Now we have a [reactive variable](../reference/reactivity.md) that keeps track of our value for degrees (in fahrenheit). Let's convert it to celsius! +This _seems_ to work at first glance, but you'll find out quickly that the value of the input isn't fully synchronized. This is because in HTML, `value=` actually refers to the [_default_ value](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input#value) of the input and not its current value. This is why instead, we should leverage the [controllable](../reference/native-tag.md#change-handlers) pattern with `Change` handlers. -> [!NOTE] -> For more control over the `` value, we could have used Marko's [controllable](../reference/native-tag.md#change-handlers) pattern. +```marko + + + +
It's ${degF}°F
+``` + +Because this is such a common pattern, Marko provides a [shorthand](../reference/language.md#shorthand-change-handlers-two-way-binding) for it! + +```marko + + + +
It's ${degF}°F
+``` ## Adding Computed Values -To do this, we can use a `` tag: +Now we can use [the `` tag](../reference/core-tag.md#const) to convert to celsius! ```marko - +
${degF}°F ↔ ${degC.toFixed(1)}°C
``` +Since `degC` is a [tag variable](../reference/language.md#tag-variables), its changes also propagate every time `degF` is updated. + ## Using Conditionals Now that we have a reactive variable, let's see what else we can do! Maybe some notes about the temperature, using [conditional tags](../reference/core-tag.md#if--else)? @@ -67,9 +81,7 @@ Now that we have a reactive variable, let's see what else we can do! Maybe some - +
${degF}°F ↔ ${degC.toFixed(1)}°C
@@ -93,9 +105,7 @@ Or what about a temperature gauge, with some fancy CSS? - +
${degF}°F ↔ ${degC.toFixed(1)}°C
@@ -136,9 +146,7 @@ Actually, this is getting a little bit too complex to all put in one place. Mayb - +
${degF}°F ↔ ${degC.toFixed(1)}°C
diff --git a/package-lock.json b/package-lock.json index 2a7e1568..6b2bd067 100644 --- a/package-lock.json +++ b/package-lock.json @@ -223,6 +223,7 @@ "integrity": "sha512-y1IOpG6OSmTpGg/CT0YBb/EAhR2nsC18QWp9Jy8HO9iGySpcwaTvs5kHa17daP3BMTwWyaX9/1tDTDQshZzXdg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@algolia/client-common": "5.49.2", "@algolia/requester-browser-xhr": "5.49.2", @@ -599,6 +600,7 @@ "integrity": "sha512-Tdfx4eH2uS+gv9V9NCr3Rz+c7RSS6ntXp3Blliud18ibRUlRxO9dTaOjG4iv4x0nAmMeedP1ORkEpeXSkh2QiQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=20" } @@ -680,7 +682,8 @@ "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.1.1.tgz", "integrity": "sha512-y/Vgo6qY08e1t9OqR56qjoFLBCpi4QfWMf2qzD1l9omRZwvSMQGRPz4x0bxkkkU4oocMAeztjzCsmLew//c/8w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@cspell/dict-dart": { "version": "2.3.2", @@ -820,14 +823,16 @@ "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.15.tgz", "integrity": "sha512-GJYnYKoD9fmo2OI0aySEGZOjThnx3upSUvV7mmqUu8oG+mGgzqm82P/f7OqsuvTaInZZwZbo+PwJQd/yHcyFIw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@cspell/dict-html-symbol-entities": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.5.tgz", "integrity": "sha512-429alTD4cE0FIwpMucvSN35Ld87HCyuM8mF731KU5Rm4Je2SG6hmVx7nkBsLyrmH3sQukTcr1GaiZsiEg8svPA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@cspell/dict-java": { "version": "5.0.12", @@ -1025,7 +1030,8 @@ "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.2.3.tgz", "integrity": "sha512-zXh1wYsNljQZfWWdSPYwQhpwiuW0KPW1dSd8idjMRvSD0aSvWWHoWlrMsmZeRl4qM4QCEAjua8+cjflm41cQBg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@cspell/dict-vue": { "version": "3.0.5", @@ -1207,6 +1213,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" }, @@ -1230,6 +1237,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" } @@ -2545,7 +2553,6 @@ "os": [ "aix" ], - "peer": true, "engines": { "node": ">=18" } @@ -2563,7 +2570,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -2581,7 +2587,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -2599,7 +2604,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -2617,7 +2621,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=18" } @@ -2635,7 +2638,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=18" } @@ -2653,7 +2655,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -2671,7 +2672,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -2689,7 +2689,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2707,7 +2706,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2725,7 +2723,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2743,7 +2740,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2761,7 +2757,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2779,7 +2774,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2797,7 +2791,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2815,7 +2808,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2833,7 +2825,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -2851,7 +2842,6 @@ "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -2869,7 +2859,6 @@ "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -2887,7 +2876,6 @@ "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -2905,7 +2893,6 @@ "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -2923,7 +2910,6 @@ "os": [ "openharmony" ], - "peer": true, "engines": { "node": ">=18" } @@ -2941,7 +2927,6 @@ "os": [ "sunos" ], - "peer": true, "engines": { "node": ">=18" } @@ -2959,7 +2944,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -2977,7 +2961,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -2995,7 +2978,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -3190,6 +3172,7 @@ "integrity": "sha512-OT5Tk8RewII3cLLgptbsxq8DX1/HZEKMPjsKbOlVVWQGK5bAYnhDdXWibEXGr9kui8v7vdQZai5zqnHMt+gi9w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@luxass/strip-json-comments": "^1.4.0", "complain": "^1.6.1", @@ -3228,6 +3211,7 @@ "integrity": "sha512-En0e+mihNUdg8Xpprj77VUqLD9NlGTmRLPG2jmsJ1rZhFUpTaPMauvBG1FQ8AzqnKSqGdUjcro4N3FOldaCxKg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@marko/run-explorer": "^2.0.1", "@marko/vite": "^5.4.2", @@ -3642,9 +3626,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3659,9 +3640,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3676,9 +3654,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3693,9 +3668,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3710,9 +3682,6 @@ "arm64" ], "dev": true, - "libc": [ - null - ], "license": "MIT", "optional": true, "os": [ @@ -3727,9 +3696,6 @@ "x64" ], "dev": true, - "libc": [ - null - ], "license": "MIT", "optional": true, "os": [ @@ -3878,9 +3844,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3902,9 +3865,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3926,9 +3886,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3950,9 +3907,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -3974,9 +3928,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -3998,9 +3949,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4247,9 +4195,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4264,9 +4209,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4281,9 +4223,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4298,9 +4237,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4315,9 +4251,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4332,9 +4265,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4349,9 +4279,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4366,9 +4293,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4383,9 +4307,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4400,9 +4321,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4417,9 +4335,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4434,9 +4349,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -4451,9 +4363,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -4716,6 +4625,7 @@ "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.18.0" } @@ -4753,6 +4663,7 @@ "integrity": "sha512-1K0wtDaRONwfhL4h8bbJ9qTjmY6rhGgRvvagXkMBsAOMNr+3Q2SffHECh9DIuNVrMA1JwA0zCwhyepgBZVakng==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@algolia/abtesting": "1.15.2", "@algolia/client-abtesting": "5.49.2", @@ -4961,6 +4872,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -8318,6 +8230,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -9123,6 +9036,7 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -9155,6 +9069,7 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -9749,7 +9664,6 @@ "arm" ], "dev": true, - "libc": "glibc", "license": "MIT", "optional": true, "os": [ @@ -9767,7 +9681,6 @@ "arm64" ], "dev": true, - "libc": "glibc", "license": "MIT", "optional": true, "os": [ @@ -9785,7 +9698,6 @@ "arm" ], "dev": true, - "libc": "musl", "license": "MIT", "optional": true, "os": [ @@ -9803,7 +9715,6 @@ "arm64" ], "dev": true, - "libc": "musl", "license": "MIT", "optional": true, "os": [ @@ -9821,7 +9732,6 @@ "riscv64" ], "dev": true, - "libc": "musl", "license": "MIT", "optional": true, "os": [ @@ -9839,7 +9749,6 @@ "x64" ], "dev": true, - "libc": "musl", "license": "MIT", "optional": true, "os": [ @@ -9857,7 +9766,6 @@ "riscv64" ], "dev": true, - "libc": "glibc", "license": "MIT", "optional": true, "os": [ @@ -9875,7 +9783,6 @@ "x64" ], "dev": true, - "libc": "glibc", "license": "MIT", "optional": true, "os": [ @@ -10432,6 +10339,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -10719,6 +10627,7 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -11331,6 +11240,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" },