rip-lang 3.10.6 → 3.10.7

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 CHANGED
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.10.6-blue.svg" alt="Version"></a>
12
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/version-3.10.7-blue.svg" alt="Version"></a>
13
13
  <a href="#zero-dependencies"><img src="https://img.shields.io/badge/dependencies-ZERO-brightgreen.svg" alt="Dependencies"></a>
14
14
  <a href="#"><img src="https://img.shields.io/badge/tests-1%2C243%2F1%2C243-brightgreen.svg" alt="Tests"></a>
15
15
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
package/docs/RIP-LANG.md CHANGED
@@ -322,6 +322,7 @@ Multiple lines
322
322
  | `*` | Merge assign | `*obj = {a: 1}` | `Object.assign(obj, {a: 1})` |
323
323
  | `not in` | Not in | `x not in arr` | Negated membership test |
324
324
  | `not of` | Not of | `k not of obj` | Negated key existence |
325
+ | `<=>` | Two-way bind | `value <=> name` | Bidirectional reactive binding (render blocks) |
325
326
 
326
327
  ## Assignment Operators
327
328
 
@@ -915,6 +916,110 @@ declare const count: Signal<number>;
915
916
  declare const doubled: Computed<number>;
916
917
  ```
917
918
 
919
+ ## Two-Way Binding (`<=>`)
920
+
921
+ The `<=>` operator creates bidirectional reactive bindings inside render blocks.
922
+ It connects a parent's reactive state to a child element or component — changes
923
+ flow in both directions automatically. This is a Rip original.
924
+
925
+ ### With HTML Elements
926
+
927
+ ```coffee
928
+ export Form = component
929
+ @name := ''
930
+ @age := 25
931
+ @agree := false
932
+
933
+ render
934
+ input value <=> @name # text input
935
+ input type: "number", value <=> @age # number input
936
+ input type: "checkbox", checked <=> @agree # checkbox
937
+ p "#{@name}, age #{@age}, agreed: #{@agree}"
938
+ ```
939
+
940
+ `value <=> @name` compiles to two things:
941
+ 1. **State → DOM**: an effect that sets `el.value = name` whenever `name` changes
942
+ 2. **DOM → State**: an event listener that sets `name = e.target.value` on input
943
+
944
+ The compiler auto-detects types:
945
+ - Text inputs use the `input` event and `e.target.value`
946
+ - Number/range inputs use `e.target.valueAsNumber`
947
+ - Checkboxes use the `change` event and `e.target.checked`
948
+
949
+ ### With Components
950
+
951
+ `<=>` works with custom components using the same syntax:
952
+
953
+ ```coffee
954
+ export App = component
955
+ @selected := 'viewer'
956
+ @showDialog := false
957
+ @darkMode := false
958
+
959
+ render
960
+ Select value <=> @selected
961
+ Option value: "viewer", "Viewer"
962
+ Option value: "editor", "Editor"
963
+ Option value: "admin", "Admin"
964
+ Switch checked <=> @darkMode, "Dark mode"
965
+ Dialog open <=> @showDialog
966
+ p "Are you sure?"
967
+ p "Role: #{@selected}"
968
+ ```
969
+
970
+ The parent owns the state. The child reads it and writes back to it. No
971
+ callback props, no `onChange` handlers, no `onOpenChange`, no `setValue`.
972
+
973
+ ### Why This Matters
974
+
975
+ React requires explicit `value` + `onChange` pairs for every bindable property.
976
+ This is the "controlled component" pattern — the single most tedious aspect of
977
+ React development:
978
+
979
+ ```jsx
980
+ // React: 8 lines of wiring for 4 controls
981
+ const [name, setName] = useState('');
982
+ const [role, setRole] = useState('viewer');
983
+ const [notify, setNotify] = useState(true);
984
+ const [show, setShow] = useState(false);
985
+
986
+ <input value={name} onChange={e => setName(e.target.value)} />
987
+ <Select value={role} onValueChange={setRole} />
988
+ <Switch checked={notify} onCheckedChange={setNotify} />
989
+ <Dialog open={show} onOpenChange={setShow} />
990
+ ```
991
+
992
+ Rip eliminates all of it:
993
+
994
+ ```coffee
995
+ # Rip: 4 state declarations, 4 bindings, zero callbacks
996
+ @name := ''
997
+ @role := 'viewer'
998
+ @notify := true
999
+ @show := false
1000
+
1001
+ input value <=> @name
1002
+ Select value <=> @role
1003
+ Switch checked <=> @notify
1004
+ Dialog open <=> @show
1005
+ ```
1006
+
1007
+ Vue has `v-model`. Svelte has `bind:`. But Rip's `<=>` is cleaner — it works
1008
+ uniformly across HTML elements and custom components with the same syntax, the
1009
+ same operator, and the same mental model. No framework-specific directives, no
1010
+ special component protocol. Just a reactive binding that flows both ways.
1011
+
1012
+ ### Auto-Detection
1013
+
1014
+ Even without `<=>`, the compiler auto-detects when `value:` or `checked:` is
1015
+ bound to a reactive expression and generates two-way binding automatically:
1016
+
1017
+ ```coffee
1018
+ # These are equivalent:
1019
+ input value <=> @name # explicit
1020
+ input value: @name # auto-detected (name is reactive)
1021
+ ```
1022
+
918
1023
  ---
919
1024
 
920
1025
  # 7. Async Patterns
@@ -1545,6 +1650,10 @@ a =~ /pat/ # regex match, captures in _
1545
1650
  a[/pat/, 1] # regex extract
1546
1651
  a? # existence check (a != null)
1547
1652
  a ?? b # nullish coalescing
1653
+
1654
+ # Two-way binding (render blocks)
1655
+ input value <=> @name # bidirectional reactive binding
1656
+ Dialog open <=> @show # works with components too
1548
1657
  ```
1549
1658
 
1550
1659
  ## File Templates