rasti 4.1.4 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -6
- package/dist/rasti.js +29 -17
- package/dist/rasti.js.map +1 -1
- package/dist/rasti.min.js +1 -1
- package/dist/rasti.min.js.map +1 -1
- package/es/Component.js +11 -8
- package/es/Component.js.map +1 -1
- package/es/View.js +4 -4
- package/es/View.js.map +1 -1
- package/es/index.js +1 -1
- package/es/utils/assignOption.js +26 -0
- package/es/utils/assignOption.js.map +1 -0
- package/lib/Component.cjs +11 -8
- package/lib/Component.cjs.map +1 -1
- package/lib/View.cjs +4 -4
- package/lib/View.cjs.map +1 -1
- package/lib/index.cjs +1 -1
- package/lib/utils/assignOption.cjs +28 -0
- package/lib/utils/assignOption.cjs.map +1 -0
- package/package.json +1 -1
- package/src/Component.js +11 -8
- package/src/View.js +4 -4
- package/src/utils/assignOption.js +25 -0
- package/types/Component.d.ts +11 -0
- package/es/utils/defineOwn.js +0 -17
- package/es/utils/defineOwn.js.map +0 -1
- package/lib/utils/defineOwn.cjs +0 -19
- package/lib/utils/defineOwn.cjs.map +0 -1
- package/src/utils/defineOwn.js +0 -16
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
<picture>
|
|
3
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.1
|
|
4
|
-
<img alt="Rasti.js" src="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.1
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.2.1/docs/logo-dark.svg">
|
|
4
|
+
<img alt="Rasti.js" src="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.2.1/docs/logo.svg" height="120">
|
|
5
5
|
</picture>
|
|
6
6
|
</p>
|
|
7
7
|
|
|
@@ -115,10 +115,11 @@ const Link = Component.create`
|
|
|
115
115
|
`;
|
|
116
116
|
|
|
117
117
|
// Create a Navigation component that renders Link components for each route.
|
|
118
|
+
// `html` builds a partial: a sub-template for lists and conditional blocks.
|
|
118
119
|
const Navigation = Component.create`
|
|
119
120
|
<nav>
|
|
120
|
-
${({ props,
|
|
121
|
-
({ label, href }) =>
|
|
121
|
+
${({ props, html }) => props.routes.map(
|
|
122
|
+
({ label, href }) => html`<${Link} href="${href}">${label}</${Link}>`
|
|
122
123
|
)}
|
|
123
124
|
</nav>
|
|
124
125
|
`;
|
|
@@ -376,7 +377,11 @@ const Toggle = Component.create<ToggleProps, ToggleState>`
|
|
|
376
377
|
|
|
377
378
|
Inside its own template, a component can't use `InstanceType<typeof Toggle>`: the type of `Toggle` depends on the template itself, so TypeScript reports a circular reference (TS7022).
|
|
378
379
|
|
|
379
|
-
**Content and quoted attributes** receive the component as their argument, so annotating the parameter types everything they read. A `function` that reads `this` instead needs `satisfies RenderExpression<C>`, which types `this` as well
|
|
380
|
+
**Content and quoted attributes** receive the component as their argument, so annotating the parameter types everything they read. A `function` that reads `this` instead needs `satisfies RenderExpression<C>`, which types `this` as well:
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
${(function() { return this.props.label; }) satisfies RenderExpression<ToggleComponent>}
|
|
384
|
+
```
|
|
380
385
|
|
|
381
386
|
**Unquoted handlers** receive the event first, not the component. An annotated parameter still compiles there, since the template's expressions are `any`, and fails at runtime. `satisfies` checks the function against what rasti passes it:
|
|
382
387
|
|
|
@@ -397,7 +402,7 @@ interface ListProps { items: string[]; handleSelect: (item: string) => void; }
|
|
|
397
402
|
|
|
398
403
|
class ListBase extends Component<ListProps> {
|
|
399
404
|
renderItems() {
|
|
400
|
-
return this.props.items.map((item) => this.
|
|
405
|
+
return this.props.items.map((item) => this.html`<li>${item}</li>`);
|
|
401
406
|
}
|
|
402
407
|
select(ev: MouseEvent) {
|
|
403
408
|
const li = (ev.target as HTMLElement).closest('li');
|
|
@@ -423,6 +428,15 @@ For a function passed to a child, neither helper fits — its type comes from th
|
|
|
423
428
|
handleChange=${((checked) => model.toggleAll(checked)) satisfies ToggleAllProps['handleChange']}
|
|
424
429
|
```
|
|
425
430
|
|
|
431
|
+
A quoted value is the result of a function the parent runs, so what reaches the child is what that function returns. Annotate its return type with the child's prop: that checks the value, and it types the parameters of a callback returned by a thunk, which would otherwise be an implicit `any` (TS7006):
|
|
432
|
+
|
|
433
|
+
```ts
|
|
434
|
+
// where the child was created with Component.create<HeaderProps>`...`
|
|
435
|
+
handleAddTodo="${({ model }: AppComponent): HeaderProps['handleAddTodo'] => (title) => model!.addTodo(title)}"
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
`satisfies` on the returned callback does the same, `({ model }: AppComponent) => ((title) => model!.addTodo(title)) satisfies HeaderProps['handleAddTodo']`; the return type keeps the whole contract in the signature.
|
|
439
|
+
|
|
426
440
|
### Known limitations
|
|
427
441
|
|
|
428
442
|
- **Template interpolation callbacks are `any`**. Functions in ``Component.create`...` `` templates can't be inferred from the surrounding string — type them by where they sit in the template (see [Typing template interpolations](#typing-template-interpolations)).
|
package/dist/rasti.js
CHANGED
|
@@ -755,19 +755,28 @@
|
|
|
755
755
|
Model.attributePrefix = '';
|
|
756
756
|
|
|
757
757
|
/**
|
|
758
|
-
*
|
|
759
|
-
*
|
|
760
|
-
*
|
|
761
|
-
* @param {object} target Object to
|
|
758
|
+
* Assign an option to `target`. A getter with no setter on the prototype would make the
|
|
759
|
+
* assignment throw, so in that case `key` is defined as an own data property, which
|
|
760
|
+
* shadows the getter. Any other accessor receives the value through its setter.
|
|
761
|
+
* @param {object} target Object to assign the option to.
|
|
762
762
|
* @param {string} key Property name.
|
|
763
763
|
* @param {any} value Property value.
|
|
764
764
|
* @return {object} The target object.
|
|
765
765
|
* @module
|
|
766
766
|
* @private
|
|
767
767
|
*/
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
|
|
768
|
+
const assignOption = (target, key, value) => {
|
|
769
|
+
try {
|
|
770
|
+
target[key] = value;
|
|
771
|
+
} catch (error) {
|
|
772
|
+
// Assigning to a getter with no setter throws a TypeError; anything else propagates.
|
|
773
|
+
if (!(error instanceof TypeError)) throw error;
|
|
774
|
+
Object.defineProperty(target, key, {
|
|
775
|
+
value, writable : true, enumerable : true, configurable : true
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
return target;
|
|
779
|
+
};
|
|
771
780
|
|
|
772
781
|
/*
|
|
773
782
|
* These option keys will be extended on the view instance.
|
|
@@ -838,11 +847,11 @@
|
|
|
838
847
|
// Mutable array to store handlers to be called on destroy.
|
|
839
848
|
this.destroyQueue = [];
|
|
840
849
|
this.viewOptions = [];
|
|
841
|
-
// Extend "this" with options
|
|
842
|
-
//
|
|
850
|
+
// Extend "this" with options. An option overrides a getter declared by a
|
|
851
|
+
// subclass even when the getter has no setter.
|
|
843
852
|
viewOptions.forEach(key => {
|
|
844
853
|
if (key in options) {
|
|
845
|
-
|
|
854
|
+
assignOption(this, key, options[key]);
|
|
846
855
|
this.viewOptions.push(key);
|
|
847
856
|
}
|
|
848
857
|
});
|
|
@@ -2424,11 +2433,11 @@
|
|
|
2424
2433
|
constructor(options = {}) {
|
|
2425
2434
|
super(...arguments);
|
|
2426
2435
|
this.componentOptions = [];
|
|
2427
|
-
// Extend "this" with options
|
|
2428
|
-
//
|
|
2436
|
+
// Extend "this" with options. An option overrides a getter declared by a
|
|
2437
|
+
// subclass even when the getter has no setter.
|
|
2429
2438
|
componentOptions.forEach(key => {
|
|
2430
2439
|
if (key in options) {
|
|
2431
|
-
|
|
2440
|
+
assignOption(this, key, options[key]);
|
|
2432
2441
|
this.componentOptions.push(key);
|
|
2433
2442
|
}
|
|
2434
2443
|
});
|
|
@@ -2445,6 +2454,8 @@
|
|
|
2445
2454
|
this.options = options;
|
|
2446
2455
|
// Bind `partial` method to `this`.
|
|
2447
2456
|
this.partial = this.partial.bind(this);
|
|
2457
|
+
// Expose `partial` as `html`, unless a subclass defines its own `html`.
|
|
2458
|
+
if (!('html' in this)) this.html = this.partial;
|
|
2448
2459
|
// Bind `onChange` method to `this`.
|
|
2449
2460
|
this.onChange = this.onChange.bind(this);
|
|
2450
2461
|
// Call lifecycle method.
|
|
@@ -2637,7 +2648,7 @@
|
|
|
2637
2648
|
* It will return a Partial object that preserves structure for position-based recycling.
|
|
2638
2649
|
* Components will be added as children by the parent component. Template strings literals
|
|
2639
2650
|
* will be marked as safe HTML to be rendered.
|
|
2640
|
-
* This method is bound to the component instance by default.
|
|
2651
|
+
* This method is bound to the component instance by default, and is also available as `this.html`.
|
|
2641
2652
|
* @param {TemplateStringsArray} strings - Template strings.
|
|
2642
2653
|
* @param {...any} expressions - Template expressions.
|
|
2643
2654
|
* @return {Partial} Partial object containing strings and expressions.
|
|
@@ -3192,8 +3203,8 @@
|
|
|
3192
3203
|
* // Create a navigation component. Add buttons as children. Iterate over items.
|
|
3193
3204
|
* const Navigation = Component.create`
|
|
3194
3205
|
* <nav>
|
|
3195
|
-
* ${({ props,
|
|
3196
|
-
* item =>
|
|
3206
|
+
* ${({ props, html }) => props.items.map(
|
|
3207
|
+
* item => html`<${Button}>${item.label}</${Button}>`
|
|
3197
3208
|
* )}
|
|
3198
3209
|
* </nav>
|
|
3199
3210
|
* `;
|
|
@@ -3226,7 +3237,7 @@
|
|
|
3226
3237
|
* ```javascript
|
|
3227
3238
|
* class ListBase extends Component {
|
|
3228
3239
|
* renderItems() {
|
|
3229
|
-
* return this.props.items.map(item => this.
|
|
3240
|
+
* return this.props.items.map(item => this.html`<li>${item}</li>`);
|
|
3230
3241
|
* }
|
|
3231
3242
|
* }
|
|
3232
3243
|
* const List = ListBase.create`
|
|
@@ -3325,6 +3336,7 @@
|
|
|
3325
3336
|
* @property {Model} [model] A `Model` or any emitter object containing data and business logic. The component will listen to `change` events and call `onChange` lifecycle method.
|
|
3326
3337
|
* @property {Model} [state] A `Model` or any emitter object containing data and business logic, to be used as internal state. The component will listen to `change` events and call `onChange` lifecycle method.
|
|
3327
3338
|
* @property {Model} [props] Automatically created from any options not merged to the component instance. Contains props passed from parent component as a `Model`. The component will listen to `change` events on props and call `onChange` lifecycle method. When a component with a `key` is recycled during parent re-render, new props are automatically updated and any changes trigger a re-render.
|
|
3339
|
+
* @property {Function} html Alias of {@link #module_component__partial partial}, bound to the component instance. Not set when a subclass defines its own `html`.
|
|
3328
3340
|
* @see {@link #module_component_create Component.create}
|
|
3329
3341
|
* @example
|
|
3330
3342
|
* import { Component, Model } from 'rasti';
|