git.iko.soy/rowtype-yoga/purescript-yoga-react-native
on main help sign in
WIP: React Native bindings development
commit70827650cbcaede327bfe162036f5e3ba51e317f authorMark <mark.eibes@gmail.com> dateMon 16 Feb 2026, 23:28 (7 months ago) parent8862eee
19 files changed+193−119
docs/src/Docs/Pages/DataViews.purs+5−5 docs/src/Docs/Pages/Display.purs+2−2 docs/src/Docs/Pages/FilesAndDragDrop.purs+12−12 docs/src/Docs/Pages/InputControls.purs+28−28 docs/src/Docs/Pages/LayoutPage.purs+4−4 docs/src/Docs/Pages/Overlays.purs+2−2 docs/src/Docs/Pages/RichMedia.purs+9−9 docs/src/Docs/Pages/TextEditing.purs+6−6 example-macos/src/Demo/AiMl.purs+6−10 example-macos/src/Demo/DragDropFiles.purs+7−12 example-macos/src/Demo/InputControls.purs+11−11 src/Yoga/React/Native/MacOS/DropZone.js+21−1 src/Yoga/React/Native/MacOS/DropZone.purs+11−5 src/Yoga/React/Native/MacOS/FilePicker.js+13−1 src/Yoga/React/Native/MacOS/FilePicker.purs+10−4 src/Yoga/React/Native/MacOS/MapView.js+21−1 src/Yoga/React/Native/MacOS/MapView.purs+12−3 src/Yoga/React/Native/MacOS/PopUp.js+10−1 src/Yoga/React/Native/MacOS/PopUp.purs+3−2
docs/src/Docs/Pages/DataViews.purs+5−5
@@ -23,15 +23,15 @@ tableView = L.componentDoc "nativeTableView" "Yoga.React.Native.MacOS.TableView
2323 , rows: [ ["file.txt", "4 KB"], ["image.png", "1.2 MB"] ]
2424 , headerVisible: true
2525 , alternatingRows: true
26− , onSelectRow: E.onInt "rowIndex" setSelected
26+ , onSelectRow: setSelected
2727 }"""
2828 [ propsTable
2929 [ { name: "columns", type_: "Array TableColumn", description: "Column definitions ({ id, title, width })" }
3030 , { name: "rows", type_: "Array (Array String)", description: "Row data as arrays of strings" }
3131 , { name: "headerVisible", type_: "Boolean", description: "Show column headers" }
3232 , { name: "alternatingRows", type_: "Boolean", description: "Alternating row backgrounds" }
33− , { name: "onSelectRow", type_: "EventHandler", description: "Row selection callback" }
34− , { name: "onDoubleClickRow", type_: "EventHandler", description: "Row double-click callback" }
33+ , { name: "onSelectRow", type_: "Int -> Effect Unit", description: "Row selection callback" }
34+ , { name: "onDoubleClickRow", type_: "Int -> Effect Unit", description: "Row double-click callback" }
3535 ]
3636 ]
3737
@@ -50,11 +50,11 @@ outlineView = L.componentDoc "nativeOutlineView" "Yoga.React.Native.MacOS.Outlin
5050 }
5151 ]
5252 , headerVisible: true
53− , onSelectItem: E.onString "id" setSelected
53+ , onSelectItem: setSelected
5454 }"""
5555 [ propsTable
5656 [ { name: "items", type_: "Array OutlineItem", description: "Hierarchical tree data (newtype with { id, title, sfSymbol, children })" }
5757 , { name: "headerVisible", type_: "Boolean", description: "Show header" }
58− , { name: "onSelectItem", type_: "EventHandler", description: "Item selection callback" }
58+ , { name: "onSelectItem", type_: "String -> Effect Unit", description: "Item selection callback" }
5959 ]
6060 ]
docs/src/Docs/Pages/Display.purs+2−2
@@ -111,11 +111,11 @@ pathControl = L.componentDoc "nativePathControl" "Yoga.React.Native.MacOS.PathCo
111111 """nativePathControl
112112 { url: "/Users/you/Documents"
113113 , pathStyle: T.standard
114− , onSelectPath: E.onString "url" setPath
114+ , onSelectPath: setPath
115115 }"""
116116 [ propsTable
117117 [ { name: "url", type_: "String", description: "File path URL to display" }
118118 , { name: "pathStyle", type_: "PathControlStyle", description: "Visual style of the path control" }
119− , { name: "onSelectPath", type_: "EventHandler", description: "Path segment selection callback" }
119+ , { name: "onSelectPath", type_: "String -> Effect Unit", description: "Path segment selection callback" }
120120 ]
121121 ]
docs/src/Docs/Pages/FilesAndDragDrop.purs+12−12
@@ -17,18 +17,18 @@ page =
1717filePicker :: Nut
1818filePicker = L.componentDoc "nativeFilePicker" "Yoga.React.Native.MacOS.FilePicker (nativeFilePicker)"
1919 """nativeFilePicker
20− { mode: T.open
20+ { mode: T.openFile
2121 , title: "Choose files"
2222 , sfSymbol: "doc.badge.plus"
2323 , message: "Select one or more files"
2424 , allowedTypes: ["pdf", "txt", "png"]
2525 , allowMultiple: true
2626 , canChooseDirectories: false
27− , onPickFiles: E.onStrings "files" setFiles
28− , onCancel: handler_ (pure unit)
27+ , onPickFiles: \files -> setFiles (map _.path files)
28+ , onCancel: pure unit
2929 }"""
3030 [ propsTable
31− [ { name: "mode", type_: "FilePickerMode", description: "open or save" }
31+ [ { name: "mode", type_: "FilePickerMode", description: "openFile or saveFile" }
3232 , { name: "title", type_: "String", description: "Dialog title" }
3333 , { name: "sfSymbol", type_: "String", description: "SF Symbol icon for the button" }
3434 , { name: "message", type_: "String", description: "Dialog message" }
@@ -36,8 +36,8 @@ filePicker = L.componentDoc "nativeFilePicker" "Yoga.React.Native.MacOS.FilePick
3636 , { name: "allowedTypes", type_: "Array String", description: "Allowed file extensions" }
3737 , { name: "allowMultiple", type_: "Boolean", description: "Allow selecting multiple files" }
3838 , { name: "canChooseDirectories", type_: "Boolean", description: "Allow choosing directories" }
39− , { name: "onPickFiles", type_: "EventHandler", description: "Files selected callback" }
40− , { name: "onCancel", type_: "EventHandler", description: "Cancel callback" }
39+ , { name: "onPickFiles", type_: "Array PickedFile -> Effect Unit", description: "Files selected callback ({ path, name })" }
40+ , { name: "onCancel", type_: "Effect Unit", description: "Cancel callback" }
4141 ]
4242 ]
4343
@@ -45,15 +45,15 @@ dropZone :: Nut
4545dropZone = L.componentDoc "nativeDropZone" "Yoga.React.Native.MacOS.DropZone (nativeDropZone)"
4646 """-- FFINativeComponent: takes props then children
4747nativeDropZone
48− { onFileDrop: E.onStrings "files" setFiles
49− , onFilesDragEnter: handler_ setDragActive
50− , onFilesDragExit: handler_ clearDragActive
48+ { onFileDrop: \dropped -> setFiles dropped.files
49+ , onFilesDragEnter: setDragActive
50+ , onFilesDragExit: clearDragActive
5151 }
5252 [ dropTargetContent ]"""
5353 [ propsTable
54− [ { name: "onFileDrop", type_: "EventHandler", description: "Drop callback with file paths" }
55− , { name: "onFilesDragEnter", type_: "EventHandler", description: "Drag enter callback" }
56− , { name: "onFilesDragExit", type_: "EventHandler", description: "Drag exit callback" }
54+ [ { name: "onFileDrop", type_: "DroppedData -> Effect Unit", description: "Drop callback ({ files, strings })" }
55+ , { name: "onFilesDragEnter", type_: "Effect Unit", description: "Drag enter callback" }
56+ , { name: "onFilesDragExit", type_: "Effect Unit", description: "Drag exit callback" }
5757 ]
5858 , D.p [ DA.klass_ "text-sm text-gray-400 mt-2" ]
5959 [ D.text_ "The drop target content is passed as the second argument (IsJSX)." ]
docs/src/Docs/Pages/InputControls.purs+28−28
@@ -47,11 +47,11 @@ switch :: Nut
4747switch = L.componentDoc "nativeSwitch" "Yoga.React.Native.MacOS.Switch (nativeSwitch)"
4848 """nativeSwitch
4949 { on: true
50− , onChange: E.onBool "on" setOn
50+ , onChange: setOn
5151 }"""
5252 [ propsTable
5353 [ { name: "on", type_: "Boolean", description: "Current on/off state" }
54− , { name: "onChange", type_: "EventHandler", description: "Toggle callback" }
54+ , { name: "onChange", type_: "Boolean -> Effect Unit", description: "Toggle callback" }
5555 ]
5656 ]
5757
@@ -61,7 +61,7 @@ slider = L.componentDoc "nativeSlider" "Yoga.React.Native.MacOS.Slider (nativeSl
6161 { value: 50.0
6262 , minValue: 0.0
6363 , maxValue: 100.0
64− , onChange: E.onNumber "value" setValue
64+ , onChange: setValue
6565 }"""
6666 [ propsTable
6767 [ { name: "value", type_: "Number", description: "Current value" }
@@ -69,7 +69,7 @@ slider = L.componentDoc "nativeSlider" "Yoga.React.Native.MacOS.Slider (nativeSl
6969 , { name: "maxValue", type_: "Number", description: "Range maximum" }
7070 , { name: "numberOfTickMarks", type_: "Int", description: "Number of tick marks" }
7171 , { name: "allowsTickMarkValuesOnly", type_: "Boolean", description: "Snap to tick marks only" }
72− , { name: "onChange", type_: "EventHandler", description: "Value change callback" }
72+ , { name: "onChange", type_: "Number -> Effect Unit", description: "Value change callback" }
7373 ]
7474 ]
7575
@@ -80,14 +80,14 @@ stepper = L.componentDoc "nativeStepper" "Yoga.React.Native.MacOS.Stepper (nativ
8080 , minValue: 0.0
8181 , maxValue: 10.0
8282 , increment: 1.0
83− , onChange: E.onNumber "value" setValue
83+ , onChange: setValue
8484 }"""
8585 [ propsTable
8686 [ { name: "value", type_: "Number", description: "Current value" }
8787 , { name: "minValue", type_: "Number", description: "Range minimum" }
8888 , { name: "maxValue", type_: "Number", description: "Range maximum" }
8989 , { name: "increment", type_: "Number", description: "Step increment" }
90− , { name: "onChange", type_: "EventHandler", description: "Value change callback" }
90+ , { name: "onChange", type_: "Number -> Effect Unit", description: "Value change callback" }
9191 ]
9292 ]
9393
@@ -96,13 +96,13 @@ checkbox = L.componentDoc "nativeCheckbox" "Yoga.React.Native.MacOS.Checkbox (na
9696 """nativeCheckbox
9797 { title: "Enable notifications"
9898 , checked: true
99− , onChange: E.onBool "checked" setChecked
99+ , onChange: setChecked
100100 }"""
101101 [ propsTable
102102 [ { name: "title", type_: "String", description: "Label text" }
103103 , { name: "checked", type_: "Boolean", description: "Checked state" }
104104 , { name: "enabled", type_: "Boolean", description: "Enable/disable" }
105− , { name: "onChange", type_: "EventHandler", description: "Toggle callback" }
105+ , { name: "onChange", type_: "Boolean -> Effect Unit", description: "Toggle callback" }
106106 ]
107107 ]
108108
@@ -111,13 +111,13 @@ radioButton = L.componentDoc "nativeRadioButton" "Yoga.React.Native.MacOS.RadioB
111111 """nativeRadioButton
112112 { title: "Option A"
113113 , selected: true
114− , onChange: E.onBool "selected" setSelected
114+ , onChange: \_ -> setChoice "optionA"
115115 }"""
116116 [ propsTable
117117 [ { name: "title", type_: "String", description: "Label text" }
118118 , { name: "selected", type_: "Boolean", description: "Selection state" }
119119 , { name: "enabled", type_: "Boolean", description: "Enable/disable" }
120− , { name: "onChange", type_: "EventHandler", description: "Selection callback" }
120+ , { name: "onChange", type_: "Boolean -> Effect Unit", description: "Selection callback" }
121121 ]
122122 ]
123123
@@ -127,13 +127,13 @@ segmented = L.componentDoc "nativeSegmented" "Yoga.React.Native.MacOS.Segmented
127127 { labels: ["One", "Two", "Three"]
128128 , sfSymbols: ["1.circle", "2.circle", "3.circle"]
129129 , selectedIndex: 0
130− , onChange: E.onInt "selectedIndex" setIndex
130+ , onChange: setIndex
131131 }"""
132132 [ propsTable
133133 [ { name: "labels", type_: "Array String", description: "Segment labels" }
134134 , { name: "sfSymbols", type_: "Array String", description: "SF Symbol icons per segment" }
135135 , { name: "selectedIndex", type_: "Int", description: "Currently selected index" }
136− , { name: "onChange", type_: "EventHandler", description: "Selection callback" }
136+ , { name: "onChange", type_: "Int -> Effect Unit", description: "Selection callback" }
137137 ]
138138 ]
139139
@@ -142,12 +142,12 @@ popUp = L.componentDoc "nativePopUp" "Yoga.React.Native.MacOS.PopUp (nativePopUp
142142 """nativePopUp
143143 { items: ["Small", "Medium", "Large"]
144144 , selectedIndex: 1
145− , onChange: E.onInt "selectedIndex" setIndex
145+ , onChange: setIndex
146146 }"""
147147 [ propsTable
148148 [ { name: "items", type_: "Array String", description: "Menu items" }
149149 , { name: "selectedIndex", type_: "Int", description: "Selected item index" }
150− , { name: "onChange", type_: "EventHandler", description: "Selection callback" }
150+ , { name: "onChange", type_: "Int -> Effect Unit", description: "Selection callback" }
151151 ]
152152 ]
153153
@@ -157,15 +157,15 @@ comboBox = L.componentDoc "nativeComboBox" "Yoga.React.Native.MacOS.ComboBox (na
157157 { items: ["Apple", "Banana", "Cherry"]
158158 , text: "Apple"
159159 , placeholder: "Choose a fruit..."
160− , onChangeText: E.onString "text" setText
161− , onSelectItem: E.onString "text" setText
160+ , onChangeText: setText
161+ , onSelectItem: setText
162162 }"""
163163 [ propsTable
164164 [ { name: "items", type_: "Array String", description: "Autocomplete suggestions" }
165165 , { name: "text", type_: "String", description: "Current text value" }
166166 , { name: "placeholder", type_: "String", description: "Placeholder text" }
167− , { name: "onChangeText", type_: "EventHandler", description: "Text change callback" }
168− , { name: "onSelectItem", type_: "EventHandler", description: "Item selection callback" }
167+ , { name: "onChangeText", type_: "String -> Effect Unit", description: "Text change callback" }
168+ , { name: "onSelectItem", type_: "String -> Effect Unit", description: "Item selection callback" }
169169 ]
170170 ]
171171
@@ -174,14 +174,14 @@ searchField = L.componentDoc "nativeSearchField" "Yoga.React.Native.MacOS.Search
174174 """nativeSearchField
175175 { placeholder: "Search..."
176176 , text: ""
177− , onChangeText: E.onString "text" setText
178− , onSearch: E.onString "text" \q -> search q
177+ , onChangeText: setText
178+ , onSearch: \q -> search q
179179 }"""
180180 [ propsTable
181181 [ { name: "text", type_: "String", description: "Current search text" }
182182 , { name: "placeholder", type_: "String", description: "Placeholder text" }
183− , { name: "onChangeText", type_: "EventHandler", description: "Text change callback" }
184− , { name: "onSearch", type_: "EventHandler", description: "Search submit callback" }
183+ , { name: "onChangeText", type_: "String -> Effect Unit", description: "Text change callback" }
184+ , { name: "onSearch", type_: "String -> Effect Unit", description: "Search submit callback" }
185185 ]
186186 ]
187187
@@ -190,12 +190,12 @@ tokenField = L.componentDoc "nativeTokenField" "Yoga.React.Native.MacOS.TokenFie
190190 """nativeTokenField
191191 { tokens: ["tag1", "tag2"]
192192 , placeholder: "Add tags..."
193− , onChangeTokens: E.onStrings "tokens" setTokens
193+ , onChangeTokens: setTokens
194194 }"""
195195 [ propsTable
196196 [ { name: "tokens", type_: "Array String", description: "Current tokens" }
197197 , { name: "placeholder", type_: "String", description: "Placeholder text" }
198− , { name: "onChangeTokens", type_: "EventHandler", description: "Tokens change callback" }
198+ , { name: "onChangeTokens", type_: "Array String -> Effect Unit", description: "Tokens change callback" }
199199 ]
200200 ]
201201
@@ -203,11 +203,11 @@ datePicker :: Nut
203203datePicker = L.componentDoc "nativeDatePicker" "Yoga.React.Native.MacOS.DatePicker (nativeDatePicker)"
204204 """nativeDatePicker
205205 { graphical: true
206− , onChange: E.onString "date" setDate
206+ , onChange: setDate
207207 }"""
208208 [ propsTable
209209 [ { name: "graphical", type_: "Boolean", description: "Show graphical calendar picker" }
210− , { name: "onChange", type_: "EventHandler", description: "Date change callback" }
210+ , { name: "onChange", type_: "String -> Effect Unit", description: "Date change callback" }
211211 ]
212212 ]
213213
@@ -216,12 +216,12 @@ colorWell = L.componentDoc "nativeColorWell" "Yoga.React.Native.MacOS.ColorWell
216216 """nativeColorWell
217217 { color: "#ff0000"
218218 , minimal: true
219− , onChange: E.onString "color" setColor
219+ , onChange: setColor
220220 }"""
221221 [ propsTable
222222 [ { name: "color", type_: "String", description: "Current color hex value" }
223223 , { name: "minimal", type_: "Boolean", description: "Minimal inline style" }
224− , { name: "onChange", type_: "EventHandler", description: "Color change callback" }
224+ , { name: "onChange", type_: "String -> Effect Unit", description: "Color change callback" }
225225 ]
226226 ]
227227
docs/src/Docs/Pages/LayoutPage.purs+4−4
@@ -63,12 +63,12 @@ tabView = L.componentDoc "nativeTabView" "Yoga.React.Native.MacOS.TabView (nativ
6363 , { id: "advanced", label: "Advanced" }
6464 ]
6565 , selectedItem: "general"
66− , onSelectTab: E.onString "tabId" setTab
66+ , onSelectTab: setTab
6767 }"""
6868 [ propsTable
6969 [ { name: "items", type_: "Array TabItem", description: "Tab items ({ id :: String, label :: String })" }
7070 , { name: "selectedItem", type_: "String", description: "Selected tab id" }
71− , { name: "onSelectTab", type_: "EventHandler", description: "Tab selection callback" }
71+ , { name: "onSelectTab", type_: "String -> Effect Unit", description: "Tab selection callback" }
7272 ]
7373 ]
7474
@@ -116,14 +116,14 @@ toolbar = L.componentDoc "nativeToolbar" "Yoga.React.Native.MacOS.Toolbar (nativ
116116 , selectedItem: "add"
117117 , toolbarStyle: T.unified
118118 , windowTitle: "My App"
119− , onSelectItem: E.onString "itemId" setItem
119+ , onSelectItem: setItem
120120 }"""
121121 [ propsTable
122122 [ { name: "items", type_: "Array ToolbarItem", description: "Toolbar items ({ id, label, sfSymbol })" }
123123 , { name: "selectedItem", type_: "String", description: "Selected item id" }
124124 , { name: "toolbarStyle", type_: "ToolbarStyle", description: "Toolbar visual style" }
125125 , { name: "windowTitle", type_: "String", description: "Window title" }
126− , { name: "onSelectItem", type_: "EventHandler", description: "Item selection callback" }
126+ , { name: "onSelectItem", type_: "String -> Effect Unit", description: "Item selection callback" }
127127 ]
128128 ]
129129
docs/src/Docs/Pages/Overlays.purs+2−2
@@ -77,12 +77,12 @@ nativeContextMenu
7777 [ { id: "copy", title: "Copy", sfSymbol: "doc.on.doc" }
7878 , { id: "paste", title: "Paste", sfSymbol: "doc.on.clipboard" }
7979 ]
80− , onSelectItem: E.onString "itemId" setSelected
80+ , onSelectItem: setSelected
8181 }
8282 [ rightClickTarget ]"""
8383 [ propsTable
8484 [ { name: "items", type_: "Array ContextMenuItem", description: "Menu items ({ id, title, sfSymbol })" }
85− , { name: "onSelectItem", type_: "EventHandler", description: "Item selection callback" }
85+ , { name: "onSelectItem", type_: "String -> Effect Unit", description: "Item selection callback" }
8686 ]
8787 , D.p [ DA.klass_ "text-sm text-gray-400 mt-2" ]
8888 [ D.text_ "The right-click target element is passed as the second argument (IsJSX)." ]
docs/src/Docs/Pages/RichMedia.purs+9−9
@@ -33,7 +33,7 @@ mapView = L.componentDoc "nativeMapView" "Yoga.React.Native.MacOS.MapView (nativ
3333 , subtitle: "CA"
3434 }
3535 ]
36− , onRegionChange: E.onNumber "latitude" setLat
36+ , onRegionChange: \region -> setLat region.latitude
3737 }"""
3838 [ propsTable
3939 [ { name: "latitude", type_: "Number", description: "Center latitude" }
@@ -43,8 +43,8 @@ mapView = L.componentDoc "nativeMapView" "Yoga.React.Native.MacOS.MapView (nativ
4343 , { name: "mapType", type_: "MapType", description: "Map display type" }
4444 , { name: "showsUserLocation", type_: "Boolean", description: "Show user location dot" }
4545 , { name: "annotations", type_: "Array MapAnnotation", description: "Map pins ({ latitude, longitude, title, subtitle })" }
46− , { name: "onRegionChange", type_: "EventHandler", description: "Region change callback" }
47− , { name: "onSelectAnnotation", type_: "EventHandler", description: "Annotation tap callback" }
46+ , { name: "onRegionChange", type_: "MapRegion -> Effect Unit", description: "Region change callback ({ latitude, longitude, latitudeDelta, longitudeDelta })" }
47+ , { name: "onSelectAnnotation", type_: "String -> Effect Unit", description: "Annotation tap callback" }
4848 ]
4949 ]
5050
@@ -54,13 +54,13 @@ pdfView = L.componentDoc "nativePDFView" "Yoga.React.Native.MacOS.PDFView (nativ
5454 { source: "/path/to/document.pdf"
5555 , autoScales: true
5656 , displayMode: T.singlePage
57− , onPageChange: E.onInt "page" setPage
57+ , onPageChange: setPage
5858 }"""
5959 [ propsTable
6060 [ { name: "source", type_: "String", description: "PDF file path or URL" }
6161 , { name: "autoScales", type_: "Boolean", description: "Auto-fit to view" }
6262 , { name: "displayMode", type_: "PDFDisplayMode", description: "PDF display mode" }
63− , { name: "onPageChange", type_: "EventHandler", description: "Page change callback" }
63+ , { name: "onPageChange", type_: "Int -> Effect Unit", description: "Page change callback" }
6464 ]
6565 ]
6666
@@ -68,13 +68,13 @@ webView :: Nut
6868webView = L.componentDoc "nativeWebView" "Yoga.React.Native.MacOS.WebView (nativeWebView)"
6969 """nativeWebView
7070 { url: "https://purescript.org"
71− , onNavigate: E.onString "url" setUrl
72− , onFinishLoad: handler_ onPageLoaded
71+ , onNavigate: setUrl
72+ , onFinishLoad: \url -> log ("Loaded: " <> url)
7373 }"""
7474 [ propsTable
7575 [ { name: "url", type_: "String", description: "URL to load" }
76− , { name: "onNavigate", type_: "EventHandler", description: "Navigation callback" }
77− , { name: "onFinishLoad", type_: "EventHandler", description: "Page load complete callback" }
76+ , { name: "onNavigate", type_: "String -> Effect Unit", description: "Navigation callback" }
77+ , { name: "onFinishLoad", type_: "String -> Effect Unit", description: "Page load complete callback" }
7878 ]
7979 ]
8080
docs/src/Docs/Pages/TextEditing.purs+6−6
@@ -19,8 +19,8 @@ textField = L.componentDoc "nativeTextField" "Yoga.React.Native.MacOS.TextField
1919 { text: "Hello"
2020 , placeholder: "Enter text..."
2121 , secure: false
22− , onChangeText: E.onString "text" setText
23− , onSubmit: handler_ doSubmit
22+ , onChangeText: setText
23+ , onSubmit: doSubmit
2424 }"""
2525 [ propsTable
2626 [ { name: "text", type_: "String", description: "Current text value" }
@@ -28,8 +28,8 @@ textField = L.componentDoc "nativeTextField" "Yoga.React.Native.MacOS.TextField
2828 , { name: "secure", type_: "Boolean", description: "Password field (hides input)" }
2929 , { name: "search", type_: "Boolean", description: "Search field style" }
3030 , { name: "rounded", type_: "Boolean", description: "Rounded bezel style" }
31− , { name: "onChangeText", type_: "EventHandler", description: "Text change callback" }
32− , { name: "onSubmit", type_: "EventHandler", description: "Submit callback (Enter key)" }
31+ , { name: "onChangeText", type_: "String -> Effect Unit", description: "Text change callback" }
32+ , { name: "onSubmit", type_: "String -> Effect Unit", description: "Submit callback (Enter key)" }
3333 ]
3434 ]
3535
@@ -39,12 +39,12 @@ textEditor = L.componentDoc "nativeTextEditor" "Yoga.React.Native.MacOS.TextEdit
3939 { text: "Rich text content..."
4040 , richText: true
4141 , showsRuler: false
42− , onChangeText: E.onString "text" setText
42+ , onChangeText: setText
4343 }"""
4444 [ propsTable
4545 [ { name: "text", type_: "String", description: "Current text content" }
4646 , { name: "richText", type_: "Boolean", description: "Enable rich text formatting" }
4747 , { name: "showsRuler", type_: "Boolean", description: "Show the formatting ruler" }
48− , { name: "onChangeText", type_: "EventHandler", description: "Text change callback" }
48+ , { name: "onChangeText", type_: "String -> Effect Unit", description: "Text change callback" }
4949 ]
5050 ]
example-macos/src/Demo/AiMl.purs+6−10
@@ -13,14 +13,12 @@ import Demo.Shared (DemoProps, desc, scrollWrap, sectionTitle)
1313import Effect.Aff (launchAff_)
1414import Effect.Class (liftEffect)
1515import React.Basic (JSX)
16−import React.Basic.Events (handler, handler_, unsafeEventFn)
1716import React.Basic.Hooks (useState', (/\))
1817import React.Basic.Hooks as React
1918import Yoga.React (component)
20−import Yoga.React.Native (nativeEvent, text, tw, view)
19+import Yoga.React.Native (text, tw, view)
2120import Yoga.React.Native.MacOS.Button (nativeButton)
2221import Yoga.React.Native.MacOS.CameraView (nativeCameraView)
23−import Yoga.React.Native.MacOS.Events as E
2422import Yoga.React.Native.MacOS.FilePicker (nativeFilePicker)
2523import Yoga.React.Native.MacOS.NaturalLanguage (analyzeSentiment, detectLanguage, tokenize)
2624import Yoga.React.Native.MacOS.OCR (recognizeText)
@@ -42,13 +40,11 @@ ocrDemo = component "OCRDemo" \dp -> React.do
4240 , allowedTypes: [ "public.image" ]
4341 , title: "Pick Image"
4442 , sfSymbol: "photo"
45− , onPickFiles: handler
46− (nativeEvent >>> unsafeEventFn \e -> E.getFieldArray "files" e)
47− \paths -> for_ paths \path -> do
48− setResult "Recognizing..."
49− launchAff_ do
50− r <- recognizeText path
51− liftEffect (setResult r)
43+ , onPickFiles: \files -> for_ files \file -> do
44+ setResult "Recognizing..."
45+ launchAff_ do
46+ r <- recognizeText file.path
47+ liftEffect (setResult r)
5248 , style: Style.style { height: 24.0, width: 140.0 }
5349 }
5450 ]
example-macos/src/Demo/DragDropFiles.purs+7−12
@@ -2,6 +2,7 @@ module Demo.DragDropFiles (dropZoneDemo, filePickerDemo) where
22
33import Prelude
44
5+import Data.String (joinWith)
56import Demo.Shared (DemoProps, card, desc, getFieldJSON, label, scrollWrap, sectionTitle)
67import React.Basic (JSX)
78import React.Basic.Events (handler, handler_, unsafeEventFn)
@@ -68,10 +69,8 @@ filePickerDemo = component "FilePickerDemo" \dp -> React.do
6869 , allowMultiple: true
6970 , allowedTypes: [ "public.image", "public.text" ]
7071 , message: "Select files to open"
71− , onPickFiles: handler
72− (nativeEvent >>> unsafeEventFn \e -> getFieldJSON "files" e)
73− setPicked
74− , onCancel: handler_ (setPicked "Cancelled")
72+ , onPickFiles: \files -> setPicked (joinWith ", " (map _.path files))
73+ , onCancel: setPicked "Cancelled"
7574 , style: Style.style { height: 24.0, width: 120.0 }
7675 }
7776 , nativeFilePicker
@@ -80,10 +79,8 @@ filePickerDemo = component "FilePickerDemo" \dp -> React.do
8079 , sfSymbol: "folder"
8180 , canChooseDirectories: true
8281 , message: "Select a folder"
83− , onPickFiles: handler
84− (nativeEvent >>> unsafeEventFn \e -> getFieldJSON "files" e)
85− setPicked
86− , onCancel: handler_ (setPicked "Cancelled")
82+ , onPickFiles: \files -> setPicked (joinWith ", " (map _.path files))
83+ , onCancel: setPicked "Cancelled"
8784 , style: Style.style { height: 24.0, width: 140.0, marginLeft: 8.0 }
8885 }
8986 , nativeFilePicker
@@ -93,10 +90,8 @@ filePickerDemo = component "FilePickerDemo" \dp -> React.do
9390 , defaultName: "Untitled.txt"
9491 , allowedTypes: [ "public.plain-text" ]
9592 , message: "Choose save location"
96− , onPickFiles: handler
97− (nativeEvent >>> unsafeEventFn \e -> getFieldJSON "files" e)
98− setPicked
99− , onCancel: handler_ (setPicked "Cancelled")
93+ , onPickFiles: \files -> setPicked (joinWith ", " (map _.path files))
94+ , onCancel: setPicked "Cancelled"
10095 , style: Style.style { height: 24.0, width: 120.0, marginLeft: 8.0 }
10196 }
10297 ]
example-macos/src/Demo/InputControls.purs+11−11
@@ -15,19 +15,22 @@ module Demo.InputControls
1515
1616import Prelude
1717
18+import Data.Array
19+ ( (!!)
20+ )
21+import Data.Maybe (fromMaybe)
1822import Demo.Shared (DemoProps, card, desc, label, round, scrollWrap, sectionTitle)
1923import React.Basic (JSX)
20−import React.Basic.Events (handler, unsafeEventFn)
24+
2125import React.Basic.Hooks (useState', (/\))
2226import React.Basic.Hooks as React
2327import Yoga.React (component)
24−import Yoga.React.Native (nativeEvent, text, tw, view)
28+import Yoga.React.Native (text, tw, view)
2529import Yoga.React.Native.MacOS.Button (nativeButton)
2630import Yoga.React.Native.MacOS.Checkbox (nativeCheckbox)
2731import Yoga.React.Native.MacOS.ColorWell (nativeColorWell)
2832import Yoga.React.Native.MacOS.ComboBox (nativeComboBox)
2933import Yoga.React.Native.MacOS.DatePicker (nativeDatePicker)
30−import Yoga.React.Native.MacOS.Events as E
3134import Yoga.React.Native.MacOS.LevelIndicator (nativeLevelIndicator)
3235import Yoga.React.Native.MacOS.PopUp (nativePopUp)
3336import Yoga.React.Native.MacOS.Progress (nativeProgress)
@@ -119,21 +122,18 @@ popUpDemo :: DemoProps -> JSX
119122popUpDemo = component "PopUpDemo" \dp -> React.do
120123 idx /\ setIdx <- useState' 0
121124 title /\ setTitle <- useState' "Small"
125+ let items = [ "Small", "Medium", "Large", "Extra Large" ]
122126 pure do
123127 scrollWrap dp
124128 [ sectionTitle dp.fg "Pop Up Button"
125129 , card dp.cardBg
126130 [ view { style: tw "flex-row items-center" }
127131 [ nativePopUp
128− { items: [ "Small", "Medium", "Large", "Extra Large" ]
132+ { items
129133 , selectedIndex: idx
130− , onChange: handler
131− ( nativeEvent >>> unsafeEventFn \e ->
132− { idx: E.getFieldInt "selectedIndex" e, title: E.getFieldStr "title" e }
133− )
134− \r -> do
135− setIdx r.idx
136− setTitle r.title
134+ , onChange: \i -> do
135+ setIdx i
136+ setTitle (fromMaybe "" (items !! i))
137137 , style: Style.style { height: 24.0, width: 160.0 }
138138 }
139139 , label dp.dimFg ("Selected: " <> title)
src/Yoga/React/Native/MacOS/DropZone.js+21−1
@@ -1,2 +1,22 @@
1+import React from "react";
12import { requireNativeComponent } from "react-native";
2−export const dropZoneImpl = requireNativeComponent("MacOSDropZone");
3+const NativeDropZone = requireNativeComponent("MacOSDropZone");
4+export const dropZoneImpl = React.forwardRef((props, ref) => {
5+ const nativeProps = { ...props };
6+ if (typeof props.onFileDrop === "function") {
7+ nativeProps.onFileDrop = (e) => {
8+ const data = {
9+ files: e.nativeEvent.files || [],
10+ strings: e.nativeEvent.strings || [],
11+ };
12+ props.onFileDrop(data)();
13+ };
14+ }
15+ if (typeof props.onFilesDragEnter === "function") {
16+ nativeProps.onFilesDragEnter = () => props.onFilesDragEnter();
17+ }
18+ if (typeof props.onFilesDragExit === "function") {
19+ nativeProps.onFilesDragExit = () => props.onFilesDragExit();
20+ }
21+ return React.createElement(NativeDropZone, { ...nativeProps, ref });
22+});
src/Yoga/React/Native/MacOS/DropZone.purs+11−5
@@ -1,7 +1,8 @@
1−module Yoga.React.Native.MacOS.DropZone (nativeDropZone, NativeDropZoneAttributes) where
1+module Yoga.React.Native.MacOS.DropZone (nativeDropZone, NativeDropZoneAttributes, DroppedData) where
22
3+import Prelude
4+import Effect (Effect)
35import React.Basic (ReactComponent)
4−import React.Basic.Events (EventHandler)
56import Yoga.React.Native.Attributes (BaseAttributes)
67import Yoga.React.Native.Internal (FFINativeComponent, createNativeElement)
78
@@ -10,8 +11,13 @@ foreign import dropZoneImpl :: forall props. ReactComponent props
1011nativeDropZone :: FFINativeComponent NativeDropZoneAttributes
1112nativeDropZone = createNativeElement dropZoneImpl
1213
14+type DroppedData =
15+ { files :: Array { path :: String, name :: String }
16+ , strings :: Array String
17+ }
18+
1319type NativeDropZoneAttributes = BaseAttributes
14− ( onFileDrop :: EventHandler
15− , onFilesDragEnter :: EventHandler
16− , onFilesDragExit :: EventHandler
20+ ( onFileDrop :: DroppedData -> Effect Unit
21+ , onFilesDragEnter :: Effect Unit
22+ , onFilesDragExit :: Effect Unit
1723 )
src/Yoga/React/Native/MacOS/FilePicker.js+13−1
@@ -1,2 +1,14 @@
1+import React from "react";
12import { requireNativeComponent } from "react-native";
2−export const filePickerImpl = requireNativeComponent("MacOSFilePicker");
3+const NativeFilePicker = requireNativeComponent("MacOSFilePicker");
4+export const filePickerImpl = React.forwardRef((props, ref) => {
5+ const nativeProps = { ...props };
6+ if (typeof props.onPickFiles === "function") {
7+ nativeProps.onPickFiles = (e) =>
8+ props.onPickFiles(e.nativeEvent.files || [])();
9+ }
10+ if (typeof props.onCancel === "function") {
11+ nativeProps.onCancel = () => props.onCancel();
12+ }
13+ return React.createElement(NativeFilePicker, { ...nativeProps, ref });
14+});
src/Yoga/React/Native/MacOS/FilePicker.purs+10−4
@@ -1,7 +1,8 @@
1−module Yoga.React.Native.MacOS.FilePicker (nativeFilePicker, NativeFilePickerAttributes) where
1+module Yoga.React.Native.MacOS.FilePicker (nativeFilePicker, NativeFilePickerAttributes, PickedFile) where
22
3+import Prelude
4+import Effect (Effect)
35import React.Basic (ReactComponent)
4−import React.Basic.Events (EventHandler)
56import Yoga.React.Native.Attributes (BaseAttributes)
67import Yoga.React.Native.Internal (FFINativeComponent_, createNativeElement_)
78import Yoga.React.Native.MacOS.Types (FilePickerMode)
@@ -11,6 +12,11 @@ foreign import filePickerImpl :: forall props. ReactComponent props
1112nativeFilePicker :: FFINativeComponent_ NativeFilePickerAttributes
1213nativeFilePicker = createNativeElement_ filePickerImpl
1314
15+type PickedFile =
16+ { path :: String
17+ , name :: String
18+ }
19+
1420type NativeFilePickerAttributes = BaseAttributes
1521 ( mode :: FilePickerMode
1622 , title :: String
@@ -20,6 +26,6 @@ type NativeFilePickerAttributes = BaseAttributes
2026 , allowedTypes :: Array String
2127 , allowMultiple :: Boolean
2228 , canChooseDirectories :: Boolean
23− , onPickFiles :: EventHandler
24− , onCancel :: EventHandler
29+ , onPickFiles :: Array PickedFile -> Effect Unit
30+ , onCancel :: Effect Unit
2531 )
src/Yoga/React/Native/MacOS/MapView.js+21−1
@@ -1,2 +1,22 @@
1+import React from "react";
12import { requireNativeComponent } from "react-native";
2−export const mapViewImpl = requireNativeComponent("MacOSMapView");
3+const NativeMapView = requireNativeComponent("MacOSMapView");
4+export const mapViewImpl = React.forwardRef((props, ref) => {
5+ const nativeProps = { ...props };
6+ if (typeof props.onRegionChange === "function") {
7+ nativeProps.onRegionChange = (e) => {
8+ const r = e.nativeEvent;
9+ props.onRegionChange({
10+ latitude: r.latitude ?? 0,
11+ longitude: r.longitude ?? 0,
12+ latitudeDelta: r.latitudeDelta ?? 0,
13+ longitudeDelta: r.longitudeDelta ?? 0,
14+ })();
15+ };
16+ }
17+ if (typeof props.onSelectAnnotation === "function") {
18+ nativeProps.onSelectAnnotation = (e) =>
19+ props.onSelectAnnotation(e.nativeEvent.title || "")();
20+ }
21+ return React.createElement(NativeMapView, { ...nativeProps, ref });
22+});
src/Yoga/React/Native/MacOS/MapView.purs+12−3
@@ -2,10 +2,12 @@ module Yoga.React.Native.MacOS.MapView
22 ( nativeMapView
33 , NativeMapViewAttributes
44 , MapAnnotation
5+ , MapRegion
56 ) where
67
8+import Prelude
9+import Effect (Effect)
710import React.Basic (ReactComponent)
8−import React.Basic.Events (EventHandler)
911import Yoga.React.Native.Attributes (BaseAttributes)
1012import Yoga.React.Native.Internal (FFINativeComponent_, createNativeElement_)
1113import Yoga.React.Native.MacOS.Types (MapType)
@@ -22,6 +24,13 @@ type MapAnnotation =
2224 , subtitle :: String
2325 }
2426
27+type MapRegion =
28+ { latitude :: Number
29+ , longitude :: Number
30+ , latitudeDelta :: Number
31+ , longitudeDelta :: Number
32+ }
33+
2534type NativeMapViewAttributes = BaseAttributes
2635 ( latitude :: Number
2736 , longitude :: Number
@@ -30,6 +39,6 @@ type NativeMapViewAttributes = BaseAttributes
3039 , mapType :: MapType
3140 , showsUserLocation :: Boolean
3241 , annotations :: Array MapAnnotation
33− , onRegionChange :: EventHandler
34− , onSelectAnnotation :: EventHandler
42+ , onRegionChange :: MapRegion -> Effect Unit
43+ , onSelectAnnotation :: String -> Effect Unit
3544 )
src/Yoga/React/Native/MacOS/PopUp.js+10−1
@@ -1,2 +1,11 @@
1+import React from "react";
12import { requireNativeComponent } from "react-native";
2−export const popUpImpl = requireNativeComponent("NativePopUp");
3+const NativePopUp = requireNativeComponent("NativePopUp");
4+export const popUpImpl = React.forwardRef((props, ref) => {
5+ const nativeProps = { ...props };
6+ if (typeof props.onChange === "function") {
7+ nativeProps.onChange = (e) =>
8+ props.onChange(e.nativeEvent.selectedIndex | 0)();
9+ }
10+ return React.createElement(NativePopUp, { ...nativeProps, ref });
11+});
src/Yoga/React/Native/MacOS/PopUp.purs+3−2
@@ -1,7 +1,8 @@
11module Yoga.React.Native.MacOS.PopUp (nativePopUp, NativePopUpAttributes) where
22
3+import Prelude
4+import Effect (Effect)
35import React.Basic (ReactComponent)
4−import React.Basic.Events (EventHandler)
56import Yoga.React.Native.Attributes (BaseAttributes)
67import Yoga.React.Native.Internal (FFINativeComponent_, createNativeElement_)
78
@@ -13,5 +14,5 @@ nativePopUp = createNativeElement_ popUpImpl
1314type NativePopUpAttributes = BaseAttributes
1415 ( items :: Array String
1516 , selectedIndex :: Int
16− , onChange :: EventHandler
17+ , onChange :: Int -> Effect Unit
1718 )