git.iko.soy/rowtype-yoga/purescript-yoga-react-native
on main help sign in
WIP: React Native bindings development
commit36cb947e3d33a183ff81ce08836cf4911ebb3741 authorMark <mark.eibes@gmail.com> dateMon 16 Feb 2026, 23:33 (7 months ago) parent7082765
7 files changed+203−114
docs/src/Docs/Pages/DataViews.purs+29−16 docs/src/Docs/Pages/FilesAndDragDrop.purs+8−7 docs/src/Docs/Pages/InputControls.purs+88−45 docs/src/Docs/Pages/LayoutPage.purs+10−10 docs/src/Docs/Pages/Overlays.purs+32−13 docs/src/Docs/Pages/RichMedia.purs+27−14 docs/src/Docs/Pages/TextEditing.purs+9−9
docs/src/Docs/Pages/DataViews.purs+29−16
@@ -15,15 +15,24 @@ page =
1515
1616tableView :: Nut
1717tableView = L.componentDoc "nativeTableView" "Yoga.React.Native.MacOS.TableView (nativeTableView)"
18− """nativeTableView
18+ """selected /\ setSelected <- useState' ""
19+
20+nativeTableView
1921 { columns:
20− [ { id: "name", title: "Name", width: 200.0 }
21− , { id: "size", title: "Size", width: 100.0 }
22+ [ { id: "name", title: "Name", width: 150.0 }
23+ , { id: "type", title: "Type", width: 100.0 }
24+ , { id: "size", title: "Size", width: 80.0 }
25+ ]
26+ , rows:
27+ [ [ "Main.purs", "PureScript", "12 KB" ]
28+ , [ "App.tsx", "TypeScript", "8 KB" ]
29+ , [ "style.css", "CSS", "3 KB" ]
30+ , [ "index.html", "HTML", "1 KB" ]
2231 ]
23− , rows: [ ["file.txt", "4 KB"], ["image.png", "1.2 MB"] ]
2432 , headerVisible: true
2533 , alternatingRows: true
26− , onSelectRow: setSelected
34+ , onSelectRow: \i -> setSelected ("Row " <> show i)
35+ , onDoubleClickRow: \i -> setSelected ("Double-clicked row " <> show i)
2736 }"""
2837 [ propsTable
2938 [ { name: "columns", type_: "Array TableColumn", description: "Column definitions ({ id, title, width })" }
@@ -37,20 +46,24 @@ tableView = L.componentDoc "nativeTableView" "Yoga.React.Native.MacOS.TableView
3746
3847outlineView :: Nut
3948outlineView = L.componentDoc "nativeOutlineView" "Yoga.React.Native.MacOS.OutlineView (nativeOutlineView)"
40− """nativeOutlineView
49+ """selection /\ setSelection <- useState' ""
50+let file id title = OutlineItem { id, title, sfSymbol: "doc", children: [] }
51+let folder id title children = OutlineItem { id, title, sfSymbol: "folder", children }
52+
53+nativeOutlineView
4154 { items:
42− [ OutlineItem
43− { id: "docs"
44− , title: "Documents"
45− , sfSymbol: "folder"
46− , children:
47− [ OutlineItem { id: "report", title: "report.pdf", sfSymbol: "doc", children: [] }
48− , OutlineItem { id: "notes", title: "notes.txt", sfSymbol: "doc.text", children: [] }
55+ [ folder "src" "src"
56+ [ file "main" "Main.purs"
57+ , folder "macos" "MacOS"
58+ [ file "btn" "Button.purs"
59+ , file "sl" "Slider.purs"
4960 ]
50− }
61+ ]
62+ , folder "test" "test"
63+ [ file "t1" "MacOSComponents.test.js" ]
5164 ]
52− , headerVisible: true
53− , onSelectItem: setSelected
65+ , headerVisible: false
66+ , onSelectItem: setSelection
5467 }"""
5568 [ propsTable
5669 [ { name: "items", type_: "Array OutlineItem", description: "Hierarchical tree data (newtype with { id, title, sfSymbol, children })" }
docs/src/Docs/Pages/FilesAndDragDrop.purs+8−7
@@ -16,16 +16,17 @@ page =
1616
1717filePicker :: Nut
1818filePicker = L.componentDoc "nativeFilePicker" "Yoga.React.Native.MacOS.FilePicker (nativeFilePicker)"
19− """nativeFilePicker
19+ """picked /\ setPicked <- useState' ""
20+
21+nativeFilePicker
2022 { mode: T.openFile
21− , title: "Choose files"
23+ , title: "Open Files"
2224 , sfSymbol: "doc.badge.plus"
23− , message: "Select one or more files"
24− , allowedTypes: ["pdf", "txt", "png"]
2525 , allowMultiple: true
26− , canChooseDirectories: false
27− , onPickFiles: \files -> setFiles (map _.path files)
28− , onCancel: pure unit
26+ , allowedTypes: [ "public.image", "public.text" ]
27+ , message: "Select files to open"
28+ , onPickFiles: \files -> setPicked (joinWith ", " (map _.path files))
29+ , onCancel: setPicked "Cancelled"
2930 }"""
3031 [ propsTable
3132 [ { name: "mode", type_: "FilePickerMode", description: "openFile or saveFile" }
docs/src/Docs/Pages/InputControls.purs+88−45
@@ -27,10 +27,13 @@ page =
2727
2828button :: Nut
2929button = L.componentDoc "nativeButton" "Yoga.React.Native.MacOS.Button (nativeButton)"
30− """nativeButton
31− { title: "Click me"
30+ """status /\ setStatus <- useState' "Ready"
31+
32+nativeButton
33+ { title: "Say Hello"
3234 , bezelStyle: T.push
33− , onPress: doSomething
35+ , primary: true
36+ , onPress: setStatus "Hello from PureScript!"
3437 }"""
3538 [ propsTable
3639 [ { name: "title", type_: "String", description: "Button label text (mutually exclusive with sfSymbol)" }
@@ -45,8 +48,10 @@ button = L.componentDoc "nativeButton" "Yoga.React.Native.MacOS.Button (nativeBu
4548
4649switch :: Nut
4750switch = L.componentDoc "nativeSwitch" "Yoga.React.Native.MacOS.Switch (nativeSwitch)"
48− """nativeSwitch
49− { on: true
51+ """on /\ setOn <- useState' false
52+
53+nativeSwitch
54+ { on
5055 , onChange: setOn
5156 }"""
5257 [ propsTable
@@ -57,10 +62,13 @@ switch = L.componentDoc "nativeSwitch" "Yoga.React.Native.MacOS.Switch (nativeSw
5762
5863slider :: Nut
5964slider = L.componentDoc "nativeSlider" "Yoga.React.Native.MacOS.Slider (nativeSlider)"
60− """nativeSlider
61− { value: 50.0
65+ """value /\ setValue <- useState' 50.0
66+
67+nativeSlider
68+ { value
6269 , minValue: 0.0
6370 , maxValue: 100.0
71+ , numberOfTickMarks: 11
6472 , onChange: setValue
6573 }"""
6674 [ propsTable
@@ -75,10 +83,12 @@ slider = L.componentDoc "nativeSlider" "Yoga.React.Native.MacOS.Slider (nativeSl
7583
7684stepper :: Nut
7785stepper = L.componentDoc "nativeStepper" "Yoga.React.Native.MacOS.Stepper (nativeStepper)"
78− """nativeStepper
79− { value: 1.0
86+ """value /\ setValue <- useState' 5.0
87+
88+nativeStepper
89+ { value
8090 , minValue: 0.0
81− , maxValue: 10.0
91+ , maxValue: 50.0
8292 , increment: 1.0
8393 , onChange: setValue
8494 }"""
@@ -93,10 +103,13 @@ stepper = L.componentDoc "nativeStepper" "Yoga.React.Native.MacOS.Stepper (nativ
93103
94104checkbox :: Nut
95105checkbox = L.componentDoc "nativeCheckbox" "Yoga.React.Native.MacOS.Checkbox (nativeCheckbox)"
96− """nativeCheckbox
97− { title: "Enable notifications"
98− , checked: true
99− , onChange: setChecked
106+ """a /\ setA <- useState' true
107+
108+nativeCheckbox
109+ { checked: a
110+ , title: "Enable notifications"
111+ , enabled: true
112+ , onChange: setA
100113 }"""
101114 [ propsTable
102115 [ { name: "title", type_: "String", description: "Label text" }
@@ -108,10 +121,19 @@ checkbox = L.componentDoc "nativeCheckbox" "Yoga.React.Native.MacOS.Checkbox (na
108121
109122radioButton :: Nut
110123radioButton = L.componentDoc "nativeRadioButton" "Yoga.React.Native.MacOS.RadioButton (nativeRadioButton)"
111− """nativeRadioButton
112− { title: "Option A"
113− , selected: true
114− , onChange: \_ -> setChoice "optionA"
124+ """choice /\ setChoice <- useState' "option1"
125+
126+nativeRadioButton
127+ { selected: choice == "option1"
128+ , title: "Small"
129+ , enabled: true
130+ , onChange: \_ -> setChoice "option1"
131+ }
132+nativeRadioButton
133+ { selected: choice == "option2"
134+ , title: "Medium"
135+ , enabled: true
136+ , onChange: \_ -> setChoice "option2"
115137 }"""
116138 [ propsTable
117139 [ { name: "title", type_: "String", description: "Label text" }
@@ -123,11 +145,12 @@ radioButton = L.componentDoc "nativeRadioButton" "Yoga.React.Native.MacOS.RadioB
123145
124146segmented :: Nut
125147segmented = L.componentDoc "nativeSegmented" "Yoga.React.Native.MacOS.Segmented (nativeSegmented)"
126− """nativeSegmented
127− { labels: ["One", "Two", "Three"]
128− , sfSymbols: ["1.circle", "2.circle", "3.circle"]
129− , selectedIndex: 0
130− , onChange: setIndex
148+ """mapType /\ setMapType <- useState' 0
149+
150+nativeSegmented
151+ { labels: [ "Standard", "Satellite", "Hybrid" ]
152+ , selectedIndex: mapType
153+ , onChange: setMapType
131154 }"""
132155 [ propsTable
133156 [ { name: "labels", type_: "Array String", description: "Segment labels" }
@@ -139,10 +162,16 @@ segmented = L.componentDoc "nativeSegmented" "Yoga.React.Native.MacOS.Segmented
139162
140163popUp :: Nut
141164popUp = L.componentDoc "nativePopUp" "Yoga.React.Native.MacOS.PopUp (nativePopUp)"
142− """nativePopUp
143− { items: ["Small", "Medium", "Large"]
144− , selectedIndex: 1
145− , onChange: setIndex
165+ """idx /\ setIdx <- useState' 0
166+title /\ setTitle <- useState' "Small"
167+let items = [ "Small", "Medium", "Large", "Extra Large" ]
168+
169+nativePopUp
170+ { items
171+ , selectedIndex: idx
172+ , onChange: \i -> do
173+ setIdx i
174+ setTitle (fromMaybe "" (items !! i))
146175 }"""
147176 [ propsTable
148177 [ { name: "items", type_: "Array String", description: "Menu items" }
@@ -153,12 +182,17 @@ popUp = L.componentDoc "nativePopUp" "Yoga.React.Native.MacOS.PopUp (nativePopUp
153182
154183comboBox :: Nut
155184comboBox = L.componentDoc "nativeComboBox" "Yoga.React.Native.MacOS.ComboBox (nativeComboBox)"
156− """nativeComboBox
157− { items: ["Apple", "Banana", "Cherry"]
158− , text: "Apple"
159− , placeholder: "Choose a fruit..."
160− , onChangeText: setText
161− , onSelectItem: setText
185+ """txt /\ setTxt <- useState' ""
186+result /\ setResult <- useState' ""
187+
188+nativeComboBox
189+ { items: [ "Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig", "Grape" ]
190+ , text: txt
191+ , placeholder: "Type a fruit..."
192+ , onChangeText: setTxt
193+ , onSelectItem: \t -> do
194+ setTxt t
195+ setResult ("Selected: " <> t)
162196 }"""
163197 [ propsTable
164198 [ { name: "items", type_: "Array String", description: "Autocomplete suggestions" }
@@ -171,11 +205,14 @@ comboBox = L.componentDoc "nativeComboBox" "Yoga.React.Native.MacOS.ComboBox (na
171205
172206searchField :: Nut
173207searchField = L.componentDoc "nativeSearchField" "Yoga.React.Native.MacOS.SearchField (nativeSearchField)"
174− """nativeSearchField
175− { placeholder: "Search..."
176− , text: ""
177− , onChangeText: setText
178− , onSearch: \q -> search q
208+ """query /\ setQuery <- useState' ""
209+result /\ setResult <- useState' ""
210+
211+nativeSearchField
212+ { text: query
213+ , placeholder: "Search..."
214+ , onChangeText: setQuery
215+ , onSearch: \t -> setResult ("Searched: " <> t)
179216 }"""
180217 [ propsTable
181218 [ { name: "text", type_: "String", description: "Current search text" }
@@ -187,8 +224,10 @@ searchField = L.componentDoc "nativeSearchField" "Yoga.React.Native.MacOS.Search
187224
188225tokenField :: Nut
189226tokenField = L.componentDoc "nativeTokenField" "Yoga.React.Native.MacOS.TokenField (nativeTokenField)"
190− """nativeTokenField
191− { tokens: ["tag1", "tag2"]
227+ """tokens /\ setTokens <- useState' [ "PureScript", "React", "macOS" ]
228+
229+nativeTokenField
230+ { tokens
192231 , placeholder: "Add tags..."
193232 , onChangeTokens: setTokens
194233 }"""
@@ -201,9 +240,11 @@ tokenField = L.componentDoc "nativeTokenField" "Yoga.React.Native.MacOS.TokenFie
201240
202241datePicker :: Nut
203242datePicker = L.componentDoc "nativeDatePicker" "Yoga.React.Native.MacOS.DatePicker (nativeDatePicker)"
204− """nativeDatePicker
205− { graphical: true
206− , onChange: setDate
243+ """dateText /\ setDateText <- useState' ""
244+
245+nativeDatePicker
246+ { graphical: false
247+ , onChange: setDateText
207248 }"""
208249 [ propsTable
209250 [ { name: "graphical", type_: "Boolean", description: "Show graphical calendar picker" }
@@ -213,8 +254,10 @@ datePicker = L.componentDoc "nativeDatePicker" "Yoga.React.Native.MacOS.DatePick
213254
214255colorWell :: Nut
215256colorWell = L.componentDoc "nativeColorWell" "Yoga.React.Native.MacOS.ColorWell (nativeColorWell)"
216− """nativeColorWell
217− { color: "#ff0000"
257+ """color /\ setColor <- useState' "#FF6600"
258+
259+nativeColorWell
260+ { color
218261 , minimal: true
219262 , onChange: setColor
220263 }"""
docs/src/Docs/Pages/LayoutPage.purs+10−10
@@ -57,13 +57,16 @@ nativeSplitView
5757
5858tabView :: Nut
5959tabView = L.componentDoc "nativeTabView" "Yoga.React.Native.MacOS.TabView (nativeTabView)"
60− """nativeTabView
60+ """selected /\ setSelected <- useState' "general"
61+
62+nativeTabView
6163 { items:
6264 [ { id: "general", label: "General" }
6365 , { id: "advanced", label: "Advanced" }
66+ , { id: "about", label: "About" }
6467 ]
65− , selectedItem: "general"
66− , onSelectTab: setTab
68+ , selectedItem: selected
69+ , onSelectTab: setSelected
6770 }"""
6871 [ propsTable
6972 [ { name: "items", type_: "Array TabItem", description: "Tab items ({ id :: String, label :: String })" }
@@ -109,14 +112,11 @@ sidebarLayout
109112toolbar :: Nut
110113toolbar = L.componentDoc "nativeToolbar" "Yoga.React.Native.MacOS.Toolbar (nativeToolbar)"
111114 """nativeToolbar
112− { items:
113− [ { id: "add", label: "Add", sfSymbol: "plus" }
114− , { id: "delete", label: "Delete", sfSymbol: "trash" }
115− ]
116− , selectedItem: "add"
115+ { items: []
116+ , selectedItem: ""
117117 , toolbarStyle: T.unified
118− , windowTitle: "My App"
119− , onSelectItem: setItem
118+ , windowTitle: "PureScript React Native"
119+ , onSelectItem: \_ -> pure unit
120120 }"""
121121 [ propsTable
122122 [ { name: "items", type_: "Array ToolbarItem", description: "Toolbar items ({ id, label, sfSymbol })" }
docs/src/Docs/Pages/Overlays.purs+32−13
@@ -35,12 +35,22 @@ alert = L.componentDoc "macosAlert" "Yoga.React.Native.MacOS.Alert (macosAlert)"
3535
3636sheet :: Nut
3737sheet = L.componentDoc "nativeSheet" "Yoga.React.Native.MacOS.Sheet (nativeSheet)"
38− """-- FFINativeComponent: takes props then children
38+ """visible /\ setVisible <- useState' false
39+
40+nativeButton
41+ { title: "Show Sheet"
42+ , bezelStyle: T.push
43+ , onPress: setVisible true
44+ }
3945nativeSheet
40− { visible: true
41− , onDismiss: hideSheet
46+ { visible
47+ , onDismiss: setVisible false
4248 }
43− [ sheetContent ]"""
49+ [ nativeButton
50+ { title: "Dismiss"
51+ , onPress: setVisible false
52+ }
53+ ]"""
4454 [ propsTable
4555 [ { name: "visible", type_: "Boolean", description: "Show/hide the sheet" }
4656 , { name: "onDismiss", type_: "Effect Unit", description: "Dismiss callback" }
@@ -51,14 +61,19 @@ nativeSheet
5161
5262popover :: Nut
5363popover = L.componentDoc "nativePopover" "Yoga.React.Native.MacOS.Popover (nativePopover)"
54− """-- FFINativeComponent: takes props then children
64+ """visible /\ setVisible <- useState' false
65+
5566nativePopover
56− { visible: true
57− , preferredEdge: T.maxY
67+ { visible
68+ , preferredEdge: T.bottom
5869 , behavior: T.transient
59− , onClose: hidePopover
70+ , onClose: setVisible false
6071 }
61− [ popoverContent ]"""
72+ [ nativeButton
73+ { title: if visible then "Hide" else "Show Popover"
74+ , onPress: setVisible (not visible)
75+ }
76+ ]"""
6277 [ propsTable
6378 [ { name: "visible", type_: "Boolean", description: "Show/hide" }
6479 , { name: "preferredEdge", type_: "PopoverEdge", description: "Preferred edge for popover placement" }
@@ -71,15 +86,19 @@ nativePopover
7186
7287contextMenu :: Nut
7388contextMenu = L.componentDoc "nativeContextMenu" "Yoga.React.Native.MacOS.ContextMenu (nativeContextMenu)"
74− """-- FFINativeComponent: takes props then children
89+ """result /\ setResult <- useState' ""
90+
7591nativeContextMenu
7692 { items:
77− [ { id: "copy", title: "Copy", sfSymbol: "doc.on.doc" }
93+ [ { id: "cut", title: "Cut", sfSymbol: "scissors" }
94+ , { id: "copy", title: "Copy", sfSymbol: "doc.on.doc" }
7895 , { id: "paste", title: "Paste", sfSymbol: "doc.on.clipboard" }
96+ , { id: "sep", title: "-", sfSymbol: "" }
97+ , { id: "delete", title: "Delete", sfSymbol: "trash" }
7998 ]
80− , onSelectItem: setSelected
99+ , onSelectItem: setResult
81100 }
82− [ rightClickTarget ]"""
101+ [ text_ "Right-click me!" ]"""
83102 [ propsTable
84103 [ { name: "items", type_: "Array ContextMenuItem", description: "Menu items ({ id, title, sfSymbol })" }
85104 , { name: "onSelectItem", type_: "String -> Effect Unit", description: "Item selection callback" }
docs/src/Docs/Pages/RichMedia.purs+27−14
@@ -19,21 +19,27 @@ page =
1919
2020mapView :: Nut
2121mapView = L.componentDoc "nativeMapView" "Yoga.React.Native.MacOS.MapView (nativeMapView)"
22− """nativeMapView
22+ """mapType /\ setMapType <- useState' 0
23+let mt = case mapType of
24+ 1 -> T.satelliteMap
25+ 2 -> T.hybridMap
26+ _ -> T.standardMap
27+
28+nativeMapView
2329 { latitude: 37.7749
2430 , longitude: -122.4194
2531 , latitudeDelta: 0.05
2632 , longitudeDelta: 0.05
27− , mapType: T.standard
28− , showsUserLocation: true
33+ , mapType: mt
34+ , showsUserLocation: false
2935 , annotations:
30− [ { latitude: 37.7749
31− , longitude: -122.4194
32− , title: "San Francisco"
33− , subtitle: "CA"
36+ [ { latitude: 37.7749, longitude: -122.4194
37+ , title: "San Francisco", subtitle: "California"
38+ }
39+ , { latitude: 37.8199, longitude: -122.4783
40+ , title: "Golden Gate Bridge", subtitle: ""
3441 }
3542 ]
36− , onRegionChange: \region -> setLat region.latitude
3743 }"""
3844 [ propsTable
3945 [ { name: "latitude", type_: "Number", description: "Center latitude" }
@@ -51,9 +57,9 @@ mapView = L.componentDoc "nativeMapView" "Yoga.React.Native.MacOS.MapView (nativ
5157pdfView :: Nut
5258pdfView = L.componentDoc "nativePDFView" "Yoga.React.Native.MacOS.PDFView (nativePDFView)"
5359 """nativePDFView
54− { source: "/path/to/document.pdf"
60+ { source: "https://example.com/document.pdf"
5561 , autoScales: true
56− , displayMode: T.singlePage
62+ , displayMode: T.singlePageContinuous
5763 , onPageChange: setPage
5864 }"""
5965 [ propsTable
@@ -66,10 +72,17 @@ pdfView = L.componentDoc "nativePDFView" "Yoga.React.Native.MacOS.PDFView (nativ
6672
6773webView :: Nut
6874webView = L.componentDoc "nativeWebView" "Yoga.React.Native.MacOS.WebView (nativeWebView)"
69− """nativeWebView
70− { url: "https://purescript.org"
71− , onNavigate: setUrl
72− , onFinishLoad: \url -> log ("Loaded: " <> url)
75+ """url /\ setUrl <- useState' "https://pursuit.purescript.org"
76+urlBar /\ setUrlBar <- useState' "https://pursuit.purescript.org"
77+
78+nativeTextField
79+ { text: urlBar
80+ , onChangeText: setUrlBar
81+ , onSubmit: setUrl
82+ }
83+nativeWebView
84+ { url
85+ , onFinishLoad: setUrlBar
7386 }"""
7487 [ propsTable
7588 [ { name: "url", type_: "String", description: "URL to load" }
docs/src/Docs/Pages/TextEditing.purs+9−9
@@ -15,12 +15,13 @@ page =
1515
1616textField :: Nut
1717textField = L.componentDoc "nativeTextField" "Yoga.React.Native.MacOS.TextField (nativeTextField)"
18− """nativeTextField
19− { text: "Hello"
20− , placeholder: "Enter text..."
21− , secure: false
22− , onChangeText: setText
23− , onSubmit: doSubmit
18+ """txt /\ setTxt <- useState' ""
19+
20+nativeTextField
21+ { placeholder: "Type something..."
22+ , search: true
23+ , text: txt
24+ , onChangeText: setTxt
2425 }"""
2526 [ propsTable
2627 [ { name: "text", type_: "String", description: "Current text value" }
@@ -36,10 +37,9 @@ textField = L.componentDoc "nativeTextField" "Yoga.React.Native.MacOS.TextField
3637textEditor :: Nut
3738textEditor = L.componentDoc "nativeTextEditor" "Yoga.React.Native.MacOS.TextEditor (nativeTextEditor)"
3839 """nativeTextEditor
39− { text: "Rich text content..."
40+ { text: "Welcome to the PureScript-driven native text editor."
4041 , richText: true
41− , showsRuler: false
42− , onChangeText: setText
42+ , showsRuler: true
4343 }"""
4444 [ propsTable
4545 [ { name: "text", type_: "String", description: "Current text content" }