kopular 1.1.5 → 1.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/LLM.md +4 -4
- package/README.md +31 -16
- package/package.json +1 -1
- package/src/kopular.ks +6 -0
package/LLM.md
CHANGED
|
@@ -135,10 +135,10 @@ extern class VElement {
|
|
|
135
135
|
static VElement Mount(Component component);
|
|
136
136
|
} from "kopular/velement";
|
|
137
137
|
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
138
|
+
// The runtime half of `styles from "./x.css";` (see "Scoped styles" below)
|
|
139
|
+
// — the compiler splices a call to this into the constructor. Listed here
|
|
140
|
+
// for reference: `using "kopular";` already brings it in, and declaring it
|
|
141
|
+
// yourself on top of that is a real KS4002 conflict.
|
|
142
142
|
extern class ScopedStyles {
|
|
143
143
|
static void Inject(string id, string css);
|
|
144
144
|
} from "kopular/vdom";
|
package/README.md
CHANGED
|
@@ -166,11 +166,11 @@ class Widget : Component {
|
|
|
166
166
|
```
|
|
167
167
|
|
|
168
168
|
Unlike `template from`, this **did** need real framework code — `ScopedStyles.Inject`
|
|
169
|
-
(`vdom.ks`) is the runtime half, and
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
169
|
+
(`vdom.ks`) is the runtime half, and `using "kopular";` brings it in along with everything
|
|
170
|
+
else — do **not** write your own `extern class ScopedStyles ... from "kopular/vdom";`, which
|
|
171
|
+
is now a real `KS4002 Declaration 'ScopedStyles' conflicts with a name brought in by
|
|
172
|
+
'using'`. (Before package `using` existed, every Kopular export did need a hand-written
|
|
173
|
+
`extern` in your project; that is what `using "kopular";` replaced.) It's idempotent,
|
|
174
174
|
injecting one real `<style>` per component *type* into `document.head` the first time any
|
|
175
175
|
instance of that type is constructed
|
|
176
176
|
(dedup is per-type, not per-instance — every instance's constructor calls `Inject` with
|
|
@@ -595,8 +595,17 @@ class Card : Component {
|
|
|
595
595
|
}
|
|
596
596
|
}
|
|
597
597
|
|
|
598
|
-
// Usage — the parent decides what's inside the
|
|
599
|
-
|
|
598
|
+
// Usage, from inside the parent's own Render() — the parent decides what's inside the
|
|
599
|
+
// card, Card decides the chrome around it. `this` only means anything in a method, so
|
|
600
|
+
// that is where the callback is built:
|
|
601
|
+
class Page : Component {
|
|
602
|
+
constructor() : base() { }
|
|
603
|
+
public override VElement Render() {
|
|
604
|
+
Card card = new Card(() => this.BuildCardBody());
|
|
605
|
+
return VElement.Mount(card);
|
|
606
|
+
}
|
|
607
|
+
private VElement BuildCardBody() { return VElement.Create("p"); }
|
|
608
|
+
}
|
|
600
609
|
```
|
|
601
610
|
|
|
602
611
|
Because the callback is invoked fresh on every one of `Card`'s own renders (not a value
|
|
@@ -655,7 +664,7 @@ escaped) for any dynamic string; `RawHtml` is for static markup only.
|
|
|
655
664
|
## HTTP
|
|
656
665
|
|
|
657
666
|
```ks
|
|
658
|
-
using "
|
|
667
|
+
using "kopular";
|
|
659
668
|
|
|
660
669
|
Response r = await Http.Get("/api/dogs");
|
|
661
670
|
if (r.ok) {
|
|
@@ -706,7 +715,7 @@ else avoids the problem by only wrapping JS APIs that take plain positional argu
|
|
|
706
715
|
## Forms
|
|
707
716
|
|
|
708
717
|
```ks
|
|
709
|
-
using "
|
|
718
|
+
using "kopular";
|
|
710
719
|
|
|
711
720
|
FormField<string> email = new FormField<string>("", (string v) => {
|
|
712
721
|
string? required = Validators.Required(v);
|
|
@@ -719,6 +728,7 @@ print(email.Error.Value); // "Must be a valid email"
|
|
|
719
728
|
print(email.Valid()); // false
|
|
720
729
|
|
|
721
730
|
// inside a hand-written Render(), building emailInput as a VElement:
|
|
731
|
+
VElement emailInput = VElement.Create("input");
|
|
722
732
|
emailInput.OnInput = (Event e) => {
|
|
723
733
|
email.Value.Value = e.target.value; // revalidates automatically
|
|
724
734
|
};
|
|
@@ -769,7 +779,7 @@ React's `useMemo`/Angular signals' `computed()` equivalent — a read-only value
|
|
|
769
779
|
from one or more `state<T>` sources, recomputed and re-notified whenever a source changes:
|
|
770
780
|
|
|
771
781
|
```ks
|
|
772
|
-
using "
|
|
782
|
+
using "kopular";
|
|
773
783
|
|
|
774
784
|
state<number> price = state(10);
|
|
775
785
|
state<number> qty = state(2);
|
|
@@ -806,18 +816,23 @@ commonly wrapping an `Http` call — as reactive `state<T>` a `Component` can re
|
|
|
806
816
|
`Subscribe` to, instead of hand-rolling the same three fields and try/catch every time:
|
|
807
817
|
|
|
808
818
|
```ks
|
|
809
|
-
using "
|
|
819
|
+
using "kopular";
|
|
810
820
|
|
|
811
|
-
|
|
812
|
-
|
|
821
|
+
class DogsPage : Component {
|
|
822
|
+
private Resource<Response> dogs;
|
|
823
|
+
constructor() : base() {
|
|
824
|
+
this.dogs = new Resource<Response>(Http.Get("https://dog.ceo/api/breeds/list/all"));
|
|
825
|
+
this.dogs.Status.Subscribe((AsyncStatus s) => this.Update());
|
|
826
|
+
}
|
|
827
|
+
}
|
|
813
828
|
```
|
|
814
829
|
|
|
815
830
|
```ks
|
|
816
831
|
public override VElement Render() {
|
|
817
|
-
return match dogs.Status.Value {
|
|
832
|
+
return match this.dogs.Status.Value {
|
|
818
833
|
AsyncStatus.Loading => this.BuildSpinner(),
|
|
819
|
-
AsyncStatus.Success => this.BuildList(dogs.Data.Value),
|
|
820
|
-
AsyncStatus.Failure => this.BuildError(dogs.Error.Value)
|
|
834
|
+
AsyncStatus.Success => this.BuildList(this.dogs.Data.Value),
|
|
835
|
+
AsyncStatus.Failure => this.BuildError(this.dogs.Error.Value)
|
|
821
836
|
};
|
|
822
837
|
}
|
|
823
838
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kopular",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Kopular: a small component framework for KopScript — components, reactive state, constructor-injected services, routing, real compiled templates, and HTTP, with no DI container",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/kopular.ks
CHANGED
|
@@ -156,6 +156,12 @@ extern class Validators {
|
|
|
156
156
|
static string? Max(number value, number max);
|
|
157
157
|
} from "kopular/forms";
|
|
158
158
|
|
|
159
|
+
// Chain validators into the single (T) => string? a FormField takes. Real
|
|
160
|
+
// generic free functions in forms.ks; without these declared here, a
|
|
161
|
+
// consumer using `using "kopular";` couldn't reach them at all.
|
|
162
|
+
extern (T) => string? CombineValidators2<T>((T) => string? v1, (T) => string? v2) from "kopular/forms";
|
|
163
|
+
extern (T) => string? CombineValidators3<T>((T) => string? v1, (T) => string? v2, (T) => string? v3) from "kopular/forms";
|
|
164
|
+
|
|
159
165
|
extern class Computed1<A, R> {
|
|
160
166
|
constructor(state<A> source, (A) => R compute);
|
|
161
167
|
state<R> Value { get; }
|