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 CHANGED
@@ -135,10 +135,10 @@ extern class VElement {
135
135
  static VElement Mount(Component component);
136
136
  } from "kopular/velement";
137
137
 
138
- // Only needed if you use `styles from "./x.css";` in a class body (see
139
- // "Scoped styles" below) — the compiler splices a call to this into the
140
- // constructor automatically, but (like every other Kopular export) it
141
- // still needs its own `extern` declaration; it is NOT auto-imported.
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 (like every other Kopular export) it needs its own
170
- `extern` declaration in your project `extern class ScopedStyles { static void
171
- Inject(string id, string css); } from "kopular/vdom";` it is not auto-imported just
172
- because you wrote `styles from`; omitting it is a real `KS4048 Undefined identifier
173
- 'ScopedStyles'` at the constructor the compiler spliced the call into. It's idempotent,
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 card, Card decides the chrome around it:
599
- Card card = new Card(() => this.BuildCardBody());
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 "./http";
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 "./forms";
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 "./computed";
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 "./resource";
819
+ using "kopular";
810
820
 
811
- Resource<Response> dogs = new Resource<Response>(Http.Get("https://dog.ceo/api/breeds/list/all"));
812
- dogs.Status.Subscribe((AsyncStatus s) => this.Update());
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.5",
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; }