Add wildcard support (#805)
* Dispatch `datastar-sse` event on `document` * Release note * Fix * Modify view transition * Fixes * Fixes * Add wildcard support * Add wildcard to OnSignalChange
This commit is contained in:
parent
05c751770b
commit
5ba9759fba
|
@ -17,3 +17,6 @@ Each tagged version of Datastar is accompanied by a release note. Read the [rele
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- The `datastar-sse` event is now dispatched on the `document` element, and using `data-on-datastar-sse` automatically listens for the event on the `document` ([#802](https://github.com/starfederation/datastar/issues/802)).
|
- The `datastar-sse` event is now dispatched on the `document` element, and using `data-on-datastar-sse` automatically listens for the event on the `document` ([#802](https://github.com/starfederation/datastar/issues/802)).
|
||||||
|
- The `data-on-signals-change-*` attribute key now accepts a path in which `*` can be used as a wildcard (`data-on-signals-change-foo.*`).
|
||||||
|
- The `@setAll` action now accepts one or more space-separated paths in which `*` can be used as a wildcard (`@setAll('foo.* bar.*', true)`).
|
||||||
|
- The `@toggleAll` action now accepts one or more space-separated paths in which `*` can be used as a wildcard (`@toggleAll('foo.* bar.*', true)`).
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
Datastar helps you build reactive web applications with the simplicity of server-side rendering and the power of a full-stack SPA framework.
|
Datastar helps you build reactive web applications with the simplicity of server-side rendering and the power of a full-stack SPA framework.
|
||||||
|
|
||||||
Getting started is as easy as adding a single 14.5 KiB script tag to your HTML.
|
Getting started is as easy as adding a single 14.6 KiB script tag to your HTML.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-beta.10/bundles/datastar.js"></script>
|
<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-beta.10/bundles/datastar.js"></script>
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
Datastar helps you build reactive web applications with the simplicity of server-side rendering and the power of a full-stack SPA framework.
|
Datastar helps you build reactive web applications with the simplicity of server-side rendering and the power of a full-stack SPA framework.
|
||||||
|
|
||||||
Getting started is as easy as adding a single 14.5 KiB script tag to your HTML.
|
Getting started is as easy as adding a single 14.6 KiB script tag to your HTML.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-beta.10/bundles/datastar.js"></script>
|
<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-beta.10/bundles/datastar.js"></script>
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
PluginType,
|
PluginType,
|
||||||
Requirement,
|
Requirement,
|
||||||
} from '../../../../engine/types'
|
} from '../../../../engine/types'
|
||||||
|
import { pathMatchesPattern } from '../../../../utils/paths'
|
||||||
import { modifyCasing } from '../../../../utils/text'
|
import { modifyCasing } from '../../../../utils/text'
|
||||||
import { modifyTiming } from '../../../../utils/timing'
|
import { modifyTiming } from '../../../../utils/timing'
|
||||||
import { modifyViewTransition } from '../../../../utils/view-transtions'
|
import { modifyViewTransition } from '../../../../utils/view-transtions'
|
||||||
|
@ -33,10 +34,10 @@ export const OnSignalChange: AttributePlugin = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const signalPath = modifyCasing(key, mods)
|
const pattern = modifyCasing(key, mods)
|
||||||
const signalValues = new Map<Signal, any>()
|
const signalValues = new Map<Signal, any>()
|
||||||
signals.walk((path, signal) => {
|
signals.walk((path, signal) => {
|
||||||
if (path.startsWith(signalPath)) {
|
if (pathMatchesPattern(path, pattern)) {
|
||||||
signalValues.set(signal, signal.value)
|
signalValues.set(signal, signal.value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,16 +1,25 @@
|
||||||
// Authors: Delaney Gillilan
|
// Authors: Delaney Gillilan
|
||||||
// Icon: ion:checkmark-round
|
// Icon: ion:checkmark-round
|
||||||
// Slug: Set all signals that match a regular expression
|
// Slug: Set all signals that match the signal path
|
||||||
|
// Description: Set all signals that match one or more space-separated paths in which `*` can be used as a wildcard
|
||||||
|
|
||||||
import { type ActionPlugin, PluginType } from '../../../../engine/types'
|
import { type ActionPlugin, PluginType } from '../../../../engine/types'
|
||||||
|
import { pathMatchesPattern } from '../../../../utils/paths'
|
||||||
|
import { trimDollarSignPrefix } from '../../../../utils/text'
|
||||||
|
|
||||||
export const SetAll: ActionPlugin = {
|
export const SetAll: ActionPlugin = {
|
||||||
type: PluginType.Action,
|
type: PluginType.Action,
|
||||||
name: 'setAll',
|
name: 'setAll',
|
||||||
fn: ({ signals }, prefix: string, newValue) => {
|
fn: ({ signals }, paths: string, newValue) => {
|
||||||
signals.walk((path, signal) => {
|
let patterns = paths.split(/\s+/).filter((p) => p !== '')
|
||||||
if (!path.startsWith(prefix)) return
|
patterns = patterns.map((p) => trimDollarSignPrefix(p))
|
||||||
signal.value = newValue
|
|
||||||
})
|
for (const pattern of patterns) {
|
||||||
|
signals.walk((path, signal) => {
|
||||||
|
if (pathMatchesPattern(path, pattern)) {
|
||||||
|
signal.value = newValue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,25 @@
|
||||||
// Authors: Delaney Gillilan
|
// Authors: Delaney Gillilan
|
||||||
// Icon: material-symbols:toggle-off
|
// Icon: material-symbols:toggle-off
|
||||||
// Slug: Toggle all signals that match a regular expression
|
// Slug: Toggle all signals that match the signal path
|
||||||
|
// Description: Toggle all signals that match one or more space-separated paths in which `*` can be used as a wildcard
|
||||||
|
|
||||||
import { type ActionPlugin, PluginType } from '../../../../engine/types'
|
import { type ActionPlugin, PluginType } from '../../../../engine/types'
|
||||||
|
import { pathMatchesPattern } from '../../../../utils/paths'
|
||||||
|
import { trimDollarSignPrefix } from '../../../../utils/text'
|
||||||
|
|
||||||
export const ToggleAll: ActionPlugin = {
|
export const ToggleAll: ActionPlugin = {
|
||||||
type: PluginType.Action,
|
type: PluginType.Action,
|
||||||
name: 'toggleAll',
|
name: 'toggleAll',
|
||||||
fn: ({ signals }, prefix: string) => {
|
fn: ({ signals }, paths: string) => {
|
||||||
signals.walk((path, signal) => {
|
let patterns = paths.split(/\s+/).filter((p) => p !== '')
|
||||||
if (!path.startsWith(prefix)) return
|
patterns = patterns.map((p) => trimDollarSignPrefix(p))
|
||||||
signal.value = !signal.value
|
|
||||||
})
|
for (const pattern of patterns) {
|
||||||
|
signals.walk((path, signal) => {
|
||||||
|
if (pathMatchesPattern(path, pattern)) {
|
||||||
|
signal.value = !signal.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export function pathMatchesPattern(path: string, pattern: string) {
|
||||||
|
const regex = new RegExp(
|
||||||
|
`^${pattern.replaceAll('.', '\\.').replaceAll('*', '.*')}$`,
|
||||||
|
)
|
||||||
|
|
||||||
|
return regex.test(path)
|
||||||
|
}
|
|
@ -7,8 +7,8 @@ import "time"
|
||||||
const (
|
const (
|
||||||
DatastarKey = "datastar"
|
DatastarKey = "datastar"
|
||||||
Version = "1.0.0-beta.10"
|
Version = "1.0.0-beta.10"
|
||||||
VersionClientByteSize = 40008
|
VersionClientByteSize = 40231
|
||||||
VersionClientByteSizeGzip = 14887
|
VersionClientByteSizeGzip = 14988
|
||||||
|
|
||||||
//region Default durations
|
//region Default durations
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,8 @@ func setupTests(ctx context.Context, router chi.Router) (err error) {
|
||||||
{ID: "on_signal_change"},
|
{ID: "on_signal_change"},
|
||||||
{ID: "on_signal_change_path"},
|
{ID: "on_signal_change_path"},
|
||||||
{ID: "on_signal_change_path_once"},
|
{ID: "on_signal_change_path_once"},
|
||||||
|
{ID: "on_signal_change_path_wildcard"},
|
||||||
|
{ID: "persist_signals"},
|
||||||
{ID: "plugin_name_prefix"},
|
{ID: "plugin_name_prefix"},
|
||||||
{ID: "radio_value"},
|
{ID: "radio_value"},
|
||||||
{ID: "ref"},
|
{ID: "ref"},
|
||||||
|
@ -62,6 +64,9 @@ func setupTests(ctx context.Context, router chi.Router) (err error) {
|
||||||
{ID: "remove_initiating_fragment"},
|
{ID: "remove_initiating_fragment"},
|
||||||
{ID: "select_multiple"},
|
{ID: "select_multiple"},
|
||||||
{ID: "select_single"},
|
{ID: "select_single"},
|
||||||
|
{ID: "set_all_path"},
|
||||||
|
{ID: "set_all_path_wildcard"},
|
||||||
|
{ID: "set_all_paths"},
|
||||||
{ID: "sse_error_event"},
|
{ID: "sse_error_event"},
|
||||||
{ID: "sse_events"},
|
{ID: "sse_events"},
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,5 +5,5 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUnitOnSignalChangePathOnce(t *testing.T) {
|
func TestUnitOnSignalChangePathOnce(t *testing.T) {
|
||||||
setupPageTestOnClick(t, "tests/on_signal_change_path_once")
|
setupPageTestOnLoad(t, "tests/on_signal_change_path_once")
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,5 +5,5 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUnitOnSignalChangePath(t *testing.T) {
|
func TestUnitOnSignalChangePath(t *testing.T) {
|
||||||
setupPageTestOnClick(t, "tests/on_signal_change_path")
|
setupPageTestOnLoad(t, "tests/on_signal_change_path")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package smoketests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnitOnSignalChangePathWildcard(t *testing.T) {
|
||||||
|
setupPageTestOnLoad(t, "tests/on_signal_change_path_wildcard")
|
||||||
|
}
|
|
@ -5,5 +5,5 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUnitOnSignalChange(t *testing.T) {
|
func TestUnitOnSignalChange(t *testing.T) {
|
||||||
setupPageTestOnClick(t, "tests/on_signal_change")
|
setupPageTestOnLoad(t, "tests/on_signal_change")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package smoketests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Jeffail/gabs/v2"
|
||||||
|
"github.com/go-rod/rod"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPersistSignals(t *testing.T) {
|
||||||
|
setupPageTest(t, "tests/persist_signals", func(runner runnerFn) {
|
||||||
|
runner("tests/persist_signals", func(t *testing.T, page *rod.Page) {
|
||||||
|
checkLocalStorage := func(path string) string {
|
||||||
|
fromLocalStorage := page.MustEval(`k => localStorage[k]`, "datastar")
|
||||||
|
marshalled := fromLocalStorage.String()
|
||||||
|
c, err := gabs.ParseJSON([]byte(marshalled))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
actual, ok := c.Path(path).Data().(string)
|
||||||
|
assert.True(t, ok)
|
||||||
|
return actual
|
||||||
|
}
|
||||||
|
|
||||||
|
page.MustWaitIdle()
|
||||||
|
assert.Equal(t, "", checkLocalStorage("foo"))
|
||||||
|
assert.Equal(t, "", checkLocalStorage("bar"))
|
||||||
|
assert.Equal(t, "", checkLocalStorage("baz"))
|
||||||
|
page.MustWaitIdle()
|
||||||
|
foo := page.MustElement("#foo")
|
||||||
|
bar := page.MustElement("#bar")
|
||||||
|
foo.MustInput("1")
|
||||||
|
bar.MustInput("1")
|
||||||
|
page.MustWaitIdle()
|
||||||
|
assert.Equal(t, "1", checkLocalStorage("foo"))
|
||||||
|
assert.Equal(t, "1", checkLocalStorage("bar"))
|
||||||
|
assert.Equal(t, "", checkLocalStorage("baz"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
func TestExamplePersist(t *testing.T) {
|
func TestExamplePersist(t *testing.T) {
|
||||||
setupPageTest(t, "examples/persist", func(runner runnerFn) {
|
setupPageTest(t, "examples/persist", func(runner runnerFn) {
|
||||||
|
t.Skip("skipping test, handled by unit tests")
|
||||||
runner("persist", func(t *testing.T, page *rod.Page) {
|
runner("persist", func(t *testing.T, page *rod.Page) {
|
||||||
page.MustWaitIdle()
|
page.MustWaitIdle()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package smoketests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnitSetAllPath(t *testing.T) {
|
||||||
|
setupPageTestOnLoad(t, "tests/set_all_path")
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package smoketests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnitSetAllPathWildcard(t *testing.T) {
|
||||||
|
setupPageTestOnLoad(t, "tests/set_all_path_wildcard")
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package smoketests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnitSetAllPaths(t *testing.T) {
|
||||||
|
setupPageTestOnLoad(t, "tests/set_all_paths")
|
||||||
|
}
|
|
@ -390,7 +390,7 @@ Now when the `Fetch a question` button is clicked, the server will respond with
|
||||||
|
|
||||||
### `data-indicator`
|
### `data-indicator`
|
||||||
|
|
||||||
The [`data-indicator`](/reference/attribute_plugins#data-data-indicator) attribute sets the value of a signal to `true` while the request is in flight, otherwise `false`. We can use this signal to show a loading indicator, which may be desirable for slower responses.
|
The [`data-indicator`](/reference/attribute_plugins#data-indicator) attribute sets the value of a signal to `true` while the request is in flight, otherwise `false`. We can use this signal to show a loading indicator, which may be desirable for slower responses.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div id="question"></div>
|
<div id="question"></div>
|
||||||
|
@ -454,19 +454,19 @@ Actions in Datastar are helper functions that are available in `data-*` attribut
|
||||||
|
|
||||||
### `@setAll()`
|
### `@setAll()`
|
||||||
|
|
||||||
The `@setAll()` action sets the values of multiple signals at once. It takes a path prefix that is used to match against signals, and a value to set them to, as arguments.
|
The `@setAll()` action sets the value of all matching signals to the expression provided in the second argument. The first argument can be one or more space-separated paths in which `*` can be used as a wildcard.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<button data-on-click="@setAll('form.', true)"></button>
|
<button data-on-click="@setAll('foo.*', $bar)"></button>
|
||||||
```
|
```
|
||||||
|
|
||||||
This sets the values of all signals namespaced under the `form` signal to `true`, which could be useful for enabling input fields in a form.
|
This sets the values of all signals namespaced under the `foo` signal to the value of `$bar`. This can be useful for checking multiple checkbox fields in a form, for example:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<input type="checkbox" data-bind-checkboxes.checkbox1 /> Checkbox 1
|
<input type="checkbox" data-bind-checkboxes.checkbox1 /> Checkbox 1
|
||||||
<input type="checkbox" data-bind-checkboxes.checkbox2 /> Checkbox 2
|
<input type="checkbox" data-bind-checkboxes.checkbox2 /> Checkbox 2
|
||||||
<input type="checkbox" data-bind-checkboxes.checkbox3 /> Checkbox 3
|
<input type="checkbox" data-bind-checkboxes.checkbox3 /> Checkbox 3
|
||||||
<button data-on-click="@setAll('checkboxes.', true)">Check All</button>
|
<button data-on-click="@setAll('checkboxes.*', true)">Check All</button>
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="flex flex-col items-start gap-2 p-8 alert">
|
<div class="flex flex-col items-start gap-2 p-8 alert">
|
||||||
|
@ -488,26 +488,26 @@ This sets the values of all signals namespaced under the `form` signal to `true`
|
||||||
<input type="checkbox" data-bind-checkboxes1.checkbox3 class="toggle" />
|
<input type="checkbox" data-bind-checkboxes1.checkbox3 class="toggle" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<button data-on-click="@setAll('checkboxes1.', true)" class="mt-4 btn btn-secondary">
|
<button data-on-click="@setAll('checkboxes1.*', true)" class="mt-4 btn btn-secondary">
|
||||||
Check All
|
Check All
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
### `@toggleAll()`
|
### `@toggleAll()`
|
||||||
|
|
||||||
The `@toggleAll()` action toggles the values of multiple signals at once. It takes a path prefix that is used to match against signals, as an argument.
|
The `@toggleAll()` action toggles the value of all matching signals. The first argument can be one or more space-separated paths in which `*` can be used as a wildcard.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<button data-on-click="@toggleAll('form.')"></button>
|
<button data-on-click="@toggleAll('foo.*')"></button>
|
||||||
```
|
```
|
||||||
|
|
||||||
This toggles the values of all signals containing `form.` (to either `true` or `false`), which could be useful for toggling input fields in a form.
|
This toggles the values of all signals namespaced under the `foo` signal (to either `true` or `false`). This can be useful for toggling multiple checkbox fields in a form, for example:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<input type="checkbox" data-bind-checkboxes.checkbox1 /> Checkbox 1
|
<input type="checkbox" data-bind-checkboxes.checkbox1 /> Checkbox 1
|
||||||
<input type="checkbox" data-bind-checkboxes.checkbox2 /> Checkbox 2
|
<input type="checkbox" data-bind-checkboxes.checkbox2 /> Checkbox 2
|
||||||
<input type="checkbox" data-bind-checkboxes.checkbox3 /> Checkbox 3
|
<input type="checkbox" data-bind-checkboxes.checkbox3 /> Checkbox 3
|
||||||
<button data-on-click="@toggleAll('checkboxes.')">Toggle All</button>
|
<button data-on-click="@toggleAll('checkboxes.*')">Toggle All</button>
|
||||||
```
|
```
|
||||||
|
|
||||||
<div class="flex flex-col items-start gap-2 p-8 alert">
|
<div class="flex flex-col items-start gap-2 p-8 alert">
|
||||||
|
@ -529,7 +529,7 @@ This toggles the values of all signals containing `form.` (to either `true` or `
|
||||||
<input type="checkbox" data-bind-checkboxes2.checkbox_3 class="toggle" />
|
<input type="checkbox" data-bind-checkboxes2.checkbox_3 class="toggle" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<button data-on-click="@toggleAll('checkboxes2.')" class="mt-4 btn btn-secondary">
|
<button data-on-click="@toggleAll('checkboxes2.*')" class="mt-4 btn btn-secondary">
|
||||||
Toggle All
|
Toggle All
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -137,22 +137,38 @@ Copies the provided evaluated expression to the clipboard.
|
||||||
|
|
||||||
### `@setAll()`
|
### `@setAll()`
|
||||||
|
|
||||||
Arguments: `@setAll(pathPrefix: string, value: any)`
|
Arguments: `@setAll(paths: string, value: any)`
|
||||||
|
|
||||||
Sets all the signals that start with the prefix to the expression provided in the second argument. This is useful for setting all the values of a signal namespace at once.
|
Sets the value of all matching signals to the expression provided in the second argument. The first argument can be one or more space-separated signal paths in which `*` can be used as a wildcard.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div data-on-change="@setAll('foo.', true)"></div>
|
<!-- Sets the value of `$foo` to `true` -->
|
||||||
|
<div data-signals-foo="false">
|
||||||
|
<button data-on-click="@setAll('foo', $bar)"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sets the values of `$foo` and `$bar.baz` to `true` -->
|
||||||
|
<div data-signals-foo="false" data-signals-bar.baz="false">
|
||||||
|
<button data-on-click="@setAll('foo bar.*', true)"></button>
|
||||||
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
### `@toggleAll()`
|
### `@toggleAll()`
|
||||||
|
|
||||||
Arguments: `@toggleAll(pathPrefix: string)`
|
Arguments: `@toggleAll(paths: string)`
|
||||||
|
|
||||||
Toggles all the signals that start with the prefix. This is useful for toggling all the values of a signal namespace at once.
|
Toggles the value of all matching signals. The first argument can be one or more space-separated signal paths or namespaced signal paths in which `*` can be used as a wildcard.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div data-on-click="@toggleAll('foo.')"></div>
|
<!-- Toggles the value of `$foo` -->
|
||||||
|
<div data-signals-foo="false">
|
||||||
|
<button data-on-change="@toggleAll('foo')"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Toggles the values of `$foo` and `$bar.baz` -->
|
||||||
|
<div data-signals-foo="false" data-signals-bar.baz="false">
|
||||||
|
<button data-on-click="@toggleAll('foo bar.*')"></button>
|
||||||
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
### `@fit()`
|
### `@fit()`
|
||||||
|
|
|
@ -41,7 +41,7 @@ Datastar provides the following
|
||||||
<div>
|
<div>
|
||||||
The Datastar <a href="https://marketplace.visualstudio.com/items?itemName=starfederation.datastar-vscode">VSCode
|
The Datastar <a href="https://marketplace.visualstudio.com/items?itemName=starfederation.datastar-vscode">VSCode
|
||||||
extension</a> and <a href="https://plugins.jetbrains.com/plugin/26072-datastar-support">IntelliJ plugin</a>
|
extension</a> and <a href="https://plugins.jetbrains.com/plugin/26072-datastar-support">IntelliJ plugin</a>
|
||||||
provided autocompletion for all <code>data-*</code> attributes.
|
provide autocompletion for all <code>data-*</code> attributes.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -533,15 +533,16 @@ Modifiers allow you to modify the element intersection behavior and the timing o
|
||||||
- `__half` - Triggers when half of the element is visible.
|
- `__half` - Triggers when half of the element is visible.
|
||||||
- `__full` - Triggers when the full element is visible.
|
- `__full` - Triggers when the full element is visible.
|
||||||
- `__debounce` - Debounce the event listener.
|
- `__debounce` - Debounce the event listener.
|
||||||
- `.500ms` - Debounce for 500 milliseconds.
|
- `.500ms` - Debounce for 500 milliseconds.
|
||||||
- `.1s` - Debounce for 1 second.
|
- `.1s` - Debounce for 1 second.
|
||||||
- `.leading` - Debounce with leading edge.
|
- `.leading` - Debounce with leading edge.
|
||||||
- `.notrail` - Debounce without trailing edge.
|
- `.notrail` - Debounce without trailing edge.
|
||||||
- `__throttle` - Throttle the event listener.
|
- `__throttle` - Throttle the event listener.
|
||||||
- `.500ms` - Throttle for 500 milliseconds.
|
- `.500ms` - Throttle for 500 milliseconds.
|
||||||
- `.1s` - Throttle for 1 second.
|
- `.1s` - Throttle for 1 second.
|
||||||
- `.noleading` - Throttle without leading edge.
|
- `.noleading` - Throttle without leading edge.
|
||||||
- `.trail` - Throttle with trailing edge.
|
- `.trail` - Throttle with trailing edge.
|
||||||
|
- `__viewtransition` - Wraps the expression in `document.startViewTransition()` when the View Transition API is available.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div data-on-intersect__once__full="$fullyIntersected = true"></div>
|
<div data-on-intersect__once__full="$fullyIntersected = true"></div>
|
||||||
|
@ -560,9 +561,10 @@ Runs an expression at a regular interval. The interval duration defaults to 1 se
|
||||||
Modifiers allow you to modify the interval duration.
|
Modifiers allow you to modify the interval duration.
|
||||||
|
|
||||||
- `__duration` - Sets the interval duration.
|
- `__duration` - Sets the interval duration.
|
||||||
- `.500ms` - Interval duration of 500 milliseconds.
|
- `.500ms` - Interval duration of 500 milliseconds.
|
||||||
- `.1s` - Interval duration of 1 second (default).
|
- `.1s` - Interval duration of 1 second (default).
|
||||||
- `.leading` - Execute the first interval immediately.
|
- `.leading` - Execute the first interval immediately.
|
||||||
|
- `__viewtransition` - Wraps the expression in `document.startViewTransition()` when the View Transition API is available.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div data-on-interval__duration.500ms="$count++"></div>
|
<div data-on-interval__duration.500ms="$count++"></div>
|
||||||
|
@ -581,8 +583,9 @@ Runs an expression when the element is loaded into the DOM.
|
||||||
Modifiers allow you to add a delay to the event listener.
|
Modifiers allow you to add a delay to the event listener.
|
||||||
|
|
||||||
- `__delay` - Delay the event listener.
|
- `__delay` - Delay the event listener.
|
||||||
- `.500ms` - Delay for 500 milliseconds.
|
- `.500ms` - Delay for 500 milliseconds.
|
||||||
- `.1s` - Delay for 1 second.
|
- `.1s` - Delay for 1 second.
|
||||||
|
- `__viewtransition` - Wraps the expression in `document.startViewTransition()` when the View Transition API is available.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div data-on-load__delay.500ms="$count = 1"></div>
|
<div data-on-load__delay.500ms="$count = 1"></div>
|
||||||
|
@ -601,15 +604,16 @@ Runs an expression on every [`requestAnimationFrame`](https://developer.mozilla.
|
||||||
Modifiers allow you to modify the timing of the event listener.
|
Modifiers allow you to modify the timing of the event listener.
|
||||||
|
|
||||||
- `__debounce` - Debounce the event listener.
|
- `__debounce` - Debounce the event listener.
|
||||||
- `.500ms` - Debounce for 500 milliseconds.
|
- `.500ms` - Debounce for 500 milliseconds.
|
||||||
- `.1s` - Debounce for 1 second.
|
- `.1s` - Debounce for 1 second.
|
||||||
- `.leading` - Debounce with leading edge.
|
- `.leading` - Debounce with leading edge.
|
||||||
- `.notrail` - Debounce without trailing edge.
|
- `.notrail` - Debounce without trailing edge.
|
||||||
- `__throttle` - Throttle the event listener.
|
- `__throttle` - Throttle the event listener.
|
||||||
- `.500ms` - Throttle for 500 milliseconds.
|
- `.500ms` - Throttle for 500 milliseconds.
|
||||||
- `.1s` - Throttle for 1 second.
|
- `.1s` - Throttle for 1 second.
|
||||||
- `.noleading` - Throttle without leading edge.
|
- `.noleading` - Throttle without leading edge.
|
||||||
- `.trail` - Throttle with trailing edge.
|
- `.trail` - Throttle with trailing edge.
|
||||||
|
- `__viewtransition` - Wraps the expression in `document.startViewTransition()` when the View Transition API is available.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div data-on-raf__debounce.10ms="$count++"></div>
|
<div data-on-raf__debounce.10ms="$count++"></div>
|
||||||
|
@ -629,20 +633,29 @@ A key can be provided to only trigger the event when a specific signal changes.
|
||||||
<div data-on-signal-change-foo="$fooCount++"></div>
|
<div data-on-signal-change-foo="$fooCount++"></div>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The signal path can contain `*` as a wildcard.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div data-signals-foo.bar="1"
|
||||||
|
data-on-signal-change-foo.*="$fooCount++"
|
||||||
|
></div>
|
||||||
|
```
|
||||||
|
|
||||||
#### Modifiers
|
#### Modifiers
|
||||||
|
|
||||||
Modifiers allow you to modify the timing of the event listener.
|
Modifiers allow you to modify the timing of the event listener.
|
||||||
|
|
||||||
- `__debounce` - Debounce the event listener.
|
- `__debounce` - Debounce the event listener.
|
||||||
- `.500ms` - Debounce for 500 milliseconds.
|
- `.500ms` - Debounce for 500 milliseconds.
|
||||||
- `.1s` - Debounce for 1 second.
|
- `.1s` - Debounce for 1 second.
|
||||||
- `.leading` - Debounce with leading edge.
|
- `.leading` - Debounce with leading edge.
|
||||||
- `.notrail` - Debounce without trailing edge.
|
- `.notrail` - Debounce without trailing edge.
|
||||||
- `__throttle` - Throttle the event listener.
|
- `__throttle` - Throttle the event listener.
|
||||||
- `.500ms` - Throttle for 500 milliseconds.
|
- `.500ms` - Throttle for 500 milliseconds.
|
||||||
- `.1s` - Throttle for 1 second.
|
- `.1s` - Throttle for 1 second.
|
||||||
- `.noleading` - Throttle without leading edge.
|
- `.noleading` - Throttle without leading edge.
|
||||||
- `.trail` - Throttle with trailing edge.
|
- `.trail` - Throttle with trailing edge.
|
||||||
|
- `__viewtransition` - Wraps the expression in `document.startViewTransition()` when the View Transition API is available.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div data-on-signal-change__debounce.100ms="$count++"></div>
|
<div data-on-signal-change__debounce.100ms="$count++"></div>
|
||||||
|
|
|
@ -76,9 +76,9 @@ Action plugins are used in Datastar expressions to perform specific actions.
|
||||||
|
|
||||||
| Action | Description |
|
| Action | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| [`@setAll()`](/reference/action_plugins#setall) | Sets all signals with a specific prefix to a provided value. |
|
| [`@setAll()`](/reference/action_plugins#setall) | Sets all signal to a provided value. |
|
||||||
| [`@toggleAll()`](/reference/action_plugins#toggleall) | Toggles all signals that start with a given prefix. |
|
| [`@toggleAll()`](/reference/action_plugins#toggleall) | Toggles all signal values. |
|
||||||
| [`@fit()`](/reference/action_plugins#fit) | Makes a value linearly interpolate from an original range to a new one. |
|
| [`@fit()`](/reference/action_plugins#fit) | Makes a value linearly interpolate. |
|
||||||
|
|
||||||
View the [action plugins reference](/reference/action_plugins)
|
View the [action plugins reference](/reference/action_plugins)
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
# On Signal Change
|
# On Signal Change
|
||||||
|
|
||||||
Tests that a signal change is detected.
|
Tests detecting a signal change.
|
||||||
|
|
||||||
<div data-signals="{foo: {bar: 0}, result: 0}" data-on-signal-change="$result = $foo.bar">
|
<div data-signals="{foo: {bar: 0}, result: 0}" data-on-signal-change="$result = $foo.bar" data-on-load="$foo.bar = 1">
|
||||||
<button id="clickable" data-on-click="$foo.bar = 1" class="btn">Change</button>
|
|
||||||
<hr />
|
|
||||||
Result:
|
Result:
|
||||||
<code id="result" data-text="$result"></code>
|
<code id="result" data-text="$result"></code>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
# On Signal Change Path
|
# On Signal Change Path
|
||||||
|
|
||||||
Tests that a signal change with a path is detected.
|
Tests detecting a signal change with a path.
|
||||||
|
|
||||||
<div data-signals="{foo: {bar: 0}, result: 0}" data-on-signal-change-foo="$result = $foo.bar">
|
<div data-signals="{foo: 0, result: 0}" data-on-signal-change-foo="$result = $foo" data-on-load="$foo = 1">
|
||||||
<button id="clickable" data-on-click="$foo.bar = 1" class="btn">Change</button>
|
|
||||||
<hr />
|
|
||||||
Result:
|
Result:
|
||||||
<code id="result" data-text="$result"></code>
|
<code id="result" data-text="$result"></code>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
# On Signal Change Path Once
|
# On Signal Change Path Once
|
||||||
|
|
||||||
Tests that a signal change with a path is detected and the expression is called once.
|
Tests detecting a signal change with a path, and that the expression is called once.
|
||||||
|
|
||||||
<div data-signals="{foo: {bar: 0}, result: 0}" data-on-signal-change-foo="$result++">
|
<div data-signals="{foo: {bar: 0}, result: 0}" data-on-signal-change-foo.bar="$result++" data-on-load="$foo.bar = 1">
|
||||||
<button id="clickable" data-on-click="$foo.bar = 1" class="btn">Change</button>
|
|
||||||
<hr />
|
|
||||||
Result:
|
Result:
|
||||||
<code id="result" data-text="$result"></code>
|
<code id="result" data-text="$result"></code>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
# On Signal Change Path Wildcard
|
||||||
|
|
||||||
|
Tests detecting a signal change with a path using a wildcard.
|
||||||
|
|
||||||
|
<div data-signals="{foo: {bar: 0}, result: 0}" data-on-signal-change-foo.*="$result = $foo.bar" data-on-load="$foo.bar = 1">
|
||||||
|
Result:
|
||||||
|
<code id="result" data-text="$result"></code>
|
||||||
|
<hr />
|
||||||
|
Expected result on click: <code>1</code>
|
||||||
|
</div>
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Persist Signals
|
||||||
|
|
||||||
|
Tests persisting signals.
|
||||||
|
|
||||||
|
<div data-signals="{foo: 0, bar: 0, baz: 0}" data-persist="foo bar" data-on-load="$foo = 1; $bar = 1; $baz = 1">
|
||||||
|
Expected value in local storage (in alphabetical order):
|
||||||
|
<pre><code>datastar: {"bar":1,"foo":1}</code></pre>
|
||||||
|
</div>
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Set All Path
|
||||||
|
|
||||||
|
Tests the set all action on a single path.
|
||||||
|
|
||||||
|
<div data-signals="{foo: false, result: 0}" data-on-load="@setAll('foo', true)">
|
||||||
|
Result:
|
||||||
|
<code id="result" data-text="$result = $foo ? 1 : 0"></code>
|
||||||
|
<hr />
|
||||||
|
Expected result on load: <code>1</code>
|
||||||
|
</div>
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Set All Path Wildcard
|
||||||
|
|
||||||
|
Tests the set all action on a path using a wildcard.
|
||||||
|
|
||||||
|
<div data-signals="{foo: {bar: false}, result: 0}" data-on-load="@setAll('foo.*', true)">
|
||||||
|
Result:
|
||||||
|
<code id="result" data-text="$result = $foo.bar ? 1 : 0"></code>
|
||||||
|
<hr />
|
||||||
|
Expected result on load: <code>1</code>
|
||||||
|
</div>
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Set All Path
|
||||||
|
|
||||||
|
Tests the set all action on multiple paths.
|
||||||
|
|
||||||
|
<div data-signals="{foo: false, bar: false, result: 0}" data-on-load="@setAll('foo bar', true)">
|
||||||
|
Result:
|
||||||
|
<code id="result" data-text="$result = $foo && $bar ? 1 : 0"></code>
|
||||||
|
<hr />
|
||||||
|
Expected result on load: <code>1</code>
|
||||||
|
</div>
|
Loading…
Reference in New Issue