git.iko.soy/rowtype-yoga/purescript-yoga-react-native
on main help sign in
40 files changed+664−262
since splitsnapshots
unifiedsplit
README.md+1 docs/src/Docs/Pages/DataViews.purs+32−19 docs/src/Docs/Pages/Display.purs+2−2 docs/src/Docs/Pages/FilesAndDragDrop.purs+24−19 docs/src/Docs/Pages/InputControls.purs+107−64 docs/src/Docs/Pages/LayoutPage.purs+12−12 docs/src/Docs/Pages/Overlays.purs+33−14 docs/src/Docs/Pages/RichMedia.purs+33−20 docs/src/Docs/Pages/TextEditing.purs+12−12 example-macos/src/Demo/AiMl.purs+6−10 example-macos/src/Demo/DataViews.purs+2−3 example-macos/src/Demo/DragDropFiles.purs+26−26 example-macos/src/Demo/InputControls.purs+17−17 example-macos/src/Demo/MapsDocs.purs+1−2 example-macos/src/Demo/Navigation.purs+4−1 example-macos/src/Demo/SpringAnimation.purs+123 spago.lock+10−1 spago.yaml+5−2 src/Yoga/React/Native/Animated.js+24−1 src/Yoga/React/Native/Animated.purs+9 src/Yoga/React/Native/MacOS/Checkbox.js+9−1 src/Yoga/React/Native/MacOS/Checkbox.purs+3−2 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/PDFView.js+10−1 src/Yoga/React/Native/MacOS/PDFView.purs+3−2 src/Yoga/React/Native/MacOS/PopUp.js+10−1 src/Yoga/React/Native/MacOS/PopUp.purs+3−2 src/Yoga/React/Native/MacOS/RadioButton.js+9−1 src/Yoga/React/Native/MacOS/RadioButton.purs+3−2 src/Yoga/React/Native/MacOS/Segmented.js+10−1 src/Yoga/React/Native/MacOS/Segmented.purs+3−2 src/Yoga/React/Native/MacOS/Stepper.js+9−1 src/Yoga/React/Native/MacOS/Stepper.purs+3−2 src/Yoga/React/Native/MacOS/TableView.js+14−1 src/Yoga/React/Native/MacOS/TableView.purs+4−3
README.md+1
@@ -87,3 +87,4 @@ myButton = nativeButton
8787- Node.js 20+ / Bun
8888- PureScript 0.15+ and Spago
8989- CocoaPods
90+
docs/src/Docs/Pages/DataViews.purs+32−19
@@ -15,46 +15,59 @@ 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: E.onInt "rowIndex" 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 })" }
3039 , { name: "rows", type_: "Array (Array String)", description: "Row data as arrays of strings" }
3140 , { name: "headerVisible", type_: "Boolean", description: "Show column headers" }
3241 , { 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" }
42+ , { name: "onSelectRow", type_: "Int -> Effect Unit", description: "Row selection callback" }
43+ , { name: "onDoubleClickRow", type_: "Int -> Effect Unit", description: "Row double-click callback" }
3544 ]
3645 ]
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: E.onString "id" 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 })" }
5770 , { name: "headerVisible", type_: "Boolean", description: "Show header" }
58− , { name: "onSelectItem", type_: "EventHandler", description: "Item selection callback" }
71+ , { name: "onSelectItem", type_: "String -> Effect Unit", description: "Item selection callback" }
5972 ]
6073 ]
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+24−19
@@ -16,19 +16,20 @@ page =
1616
1717filePicker :: Nut
1818filePicker = L.componentDoc "nativeFilePicker" "Yoga.React.Native.MacOS.FilePicker (nativeFilePicker)"
19− """nativeFilePicker
20− { mode: T.open
21− , title: "Choose files"
19+ """picked /\ setPicked <- useState' ""
20+
21+nativeFilePicker
22+ { mode: T.openFile
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: E.onStrings "files" setFiles
28− , onCancel: handler_ (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
31− [ { name: "mode", type_: "FilePickerMode", description: "open or save" }
32+ [ { name: "mode", type_: "FilePickerMode", description: "openFile or saveFile" }
3233 , { name: "title", type_: "String", description: "Dialog title" }
3334 , { name: "sfSymbol", type_: "String", description: "SF Symbol icon for the button" }
3435 , { name: "message", type_: "String", description: "Dialog message" }
@@ -36,24 +37,28 @@ filePicker = L.componentDoc "nativeFilePicker" "Yoga.React.Native.MacOS.FilePick
3637 , { name: "allowedTypes", type_: "Array String", description: "Allowed file extensions" }
3738 , { name: "allowMultiple", type_: "Boolean", description: "Allow selecting multiple files" }
3839 , { 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" }
40+ , { name: "onPickFiles", type_: "Array PickedFile -> Effect Unit", description: "Files selected callback ({ path, name })" }
41+ , { name: "onCancel", type_: "Effect Unit", description: "Cancel callback" }
4142 ]
4243 ]
4344
4445dropZone :: Nut
4546dropZone = L.componentDoc "nativeDropZone" "Yoga.React.Native.MacOS.DropZone (nativeDropZone)"
46− """-- FFINativeComponent: takes props then children
47+ """dropped /\ setDropped <- useState' ""
48+dragging /\ setDragging <- useState' false
49+
4750nativeDropZone
48− { onFileDrop: E.onStrings "files" setFiles
49− , onFilesDragEnter: handler_ setDragActive
50− , onFilesDragExit: handler_ clearDragActive
51+ { onFileDrop: \d -> do
52+ setDropped (joinWith ", " (map _.name d.files))
53+ setDragging false
54+ , onFilesDragEnter: setDragging true
55+ , onFilesDragExit: setDragging false
5156 }
52− [ dropTargetContent ]"""
57+ [ text_ (if dragging then "Release to drop!" else "Drop files here") ]"""
5358 [ 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" }
59+ [ { name: "onFileDrop", type_: "DroppedData -> Effect Unit", description: "Drop callback ({ files, strings })" }
60+ , { name: "onFilesDragEnter", type_: "Effect Unit", description: "Drag enter callback" }
61+ , { name: "onFilesDragExit", type_: "Effect Unit", description: "Drag exit callback" }
5762 ]
5863 , D.p [ DA.klass_ "text-sm text-gray-400 mt-2" ]
5964 [ D.text_ "The drop target content is passed as the second argument (IsJSX)." ]
docs/src/Docs/Pages/InputControls.purs+107−64
@@ -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,23 +48,28 @@ 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
50− , onChange: E.onBool "on" setOn
51+ """on /\ setOn <- useState' false
52+
53+nativeSwitch
54+ { on
55+ , onChange: setOn
5156 }"""
5257 [ propsTable
5358 [ { name: "on", type_: "Boolean", description: "Current on/off state" }
54− , { name: "onChange", type_: "EventHandler", description: "Toggle callback" }
59+ , { name: "onChange", type_: "Boolean -> Effect Unit", description: "Toggle callback" }
5560 ]
5661 ]
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
64− , onChange: E.onNumber "value" setValue
71+ , numberOfTickMarks: 11
72+ , onChange: setValue
6573 }"""
6674 [ propsTable
6775 [ { name: "value", type_: "Number", description: "Current value" }
@@ -69,159 +77,194 @@ slider = L.componentDoc "nativeSlider" "Yoga.React.Native.MacOS.Slider (nativeSl
6977 , { name: "maxValue", type_: "Number", description: "Range maximum" }
7078 , { name: "numberOfTickMarks", type_: "Int", description: "Number of tick marks" }
7179 , { name: "allowsTickMarkValuesOnly", type_: "Boolean", description: "Snap to tick marks only" }
72− , { name: "onChange", type_: "EventHandler", description: "Value change callback" }
80+ , { name: "onChange", type_: "Number -> Effect Unit", description: "Value change callback" }
7381 ]
7482 ]
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
83− , onChange: E.onNumber "value" setValue
93+ , onChange: setValue
8494 }"""
8595 [ propsTable
8696 [ { name: "value", type_: "Number", description: "Current value" }
8797 , { name: "minValue", type_: "Number", description: "Range minimum" }
8898 , { name: "maxValue", type_: "Number", description: "Range maximum" }
8999 , { name: "increment", type_: "Number", description: "Step increment" }
90− , { name: "onChange", type_: "EventHandler", description: "Value change callback" }
100+ , { name: "onChange", type_: "Number -> Effect Unit", description: "Value change callback" }
91101 ]
92102 ]
93103
94104checkbox :: Nut
95105checkbox = L.componentDoc "nativeCheckbox" "Yoga.React.Native.MacOS.Checkbox (nativeCheckbox)"
96− """nativeCheckbox
97− { title: "Enable notifications"
98− , checked: true
99− , onChange: E.onBool "checked" 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" }
103116 , { name: "checked", type_: "Boolean", description: "Checked state" }
104117 , { name: "enabled", type_: "Boolean", description: "Enable/disable" }
105− , { name: "onChange", type_: "EventHandler", description: "Toggle callback" }
118+ , { name: "onChange", type_: "Boolean -> Effect Unit", description: "Toggle callback" }
106119 ]
107120 ]
108121
109122radioButton :: Nut
110123radioButton = L.componentDoc "nativeRadioButton" "Yoga.React.Native.MacOS.RadioButton (nativeRadioButton)"
111− """nativeRadioButton
112− { title: "Option A"
113− , selected: true
114− , onChange: E.onBool "selected" setSelected
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" }
118140 , { name: "selected", type_: "Boolean", description: "Selection state" }
119141 , { name: "enabled", type_: "Boolean", description: "Enable/disable" }
120− , { name: "onChange", type_: "EventHandler", description: "Selection callback" }
142+ , { name: "onChange", type_: "Boolean -> Effect Unit", description: "Selection callback" }
121143 ]
122144 ]
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: E.onInt "selectedIndex" 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" }
134157 , { name: "sfSymbols", type_: "Array String", description: "SF Symbol icons per segment" }
135158 , { name: "selectedIndex", type_: "Int", description: "Currently selected index" }
136− , { name: "onChange", type_: "EventHandler", description: "Selection callback" }
159+ , { name: "onChange", type_: "Int -> Effect Unit", description: "Selection callback" }
137160 ]
138161 ]
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: E.onInt "selectedIndex" 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" }
149178 , { name: "selectedIndex", type_: "Int", description: "Selected item index" }
150− , { name: "onChange", type_: "EventHandler", description: "Selection callback" }
179+ , { name: "onChange", type_: "Int -> Effect Unit", description: "Selection callback" }
151180 ]
152181 ]
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: E.onString "text" setText
161− , onSelectItem: E.onString "text" 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" }
165199 , { name: "text", type_: "String", description: "Current text value" }
166200 , { 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" }
201+ , { name: "onChangeText", type_: "String -> Effect Unit", description: "Text change callback" }
202+ , { name: "onSelectItem", type_: "String -> Effect Unit", description: "Item selection callback" }
169203 ]
170204 ]
171205
172206searchField :: Nut
173207searchField = L.componentDoc "nativeSearchField" "Yoga.React.Native.MacOS.SearchField (nativeSearchField)"
174− """nativeSearchField
175− { placeholder: "Search..."
176− , text: ""
177− , onChangeText: E.onString "text" setText
178− , onSearch: E.onString "text" \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" }
182219 , { 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" }
220+ , { name: "onChangeText", type_: "String -> Effect Unit", description: "Text change callback" }
221+ , { name: "onSearch", type_: "String -> Effect Unit", description: "Search submit callback" }
185222 ]
186223 ]
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..."
193− , onChangeTokens: E.onStrings "tokens" setTokens
232+ , onChangeTokens: setTokens
194233 }"""
195234 [ propsTable
196235 [ { name: "tokens", type_: "Array String", description: "Current tokens" }
197236 , { name: "placeholder", type_: "String", description: "Placeholder text" }
198− , { name: "onChangeTokens", type_: "EventHandler", description: "Tokens change callback" }
237+ , { name: "onChangeTokens", type_: "Array String -> Effect Unit", description: "Tokens change callback" }
199238 ]
200239 ]
201240
202241datePicker :: Nut
203242datePicker = L.componentDoc "nativeDatePicker" "Yoga.React.Native.MacOS.DatePicker (nativeDatePicker)"
204− """nativeDatePicker
205− { graphical: true
206− , onChange: E.onString "date" 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" }
210− , { name: "onChange", type_: "EventHandler", description: "Date change callback" }
251+ , { name: "onChange", type_: "String -> Effect Unit", description: "Date change callback" }
211252 ]
212253 ]
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
219− , onChange: E.onString "color" setColor
262+ , onChange: setColor
220263 }"""
221264 [ propsTable
222265 [ { name: "color", type_: "String", description: "Current color hex value" }
223266 , { name: "minimal", type_: "Boolean", description: "Minimal inline style" }
224− , { name: "onChange", type_: "EventHandler", description: "Color change callback" }
267+ , { name: "onChange", type_: "String -> Effect Unit", description: "Color change callback" }
225268 ]
226269 ]
227270
docs/src/Docs/Pages/LayoutPage.purs+12−12
@@ -57,18 +57,21 @@ 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: E.onString "tabId" setTab
68+ , selectedItem: selected
69+ , onSelectTab: setSelected
6770 }"""
6871 [ propsTable
6972 [ { name: "items", type_: "Array TabItem", description: "Tab items ({ id :: String, label :: String })" }
7073 , { name: "selectedItem", type_: "String", description: "Selected tab id" }
71− , { name: "onSelectTab", type_: "EventHandler", description: "Tab selection callback" }
74+ , { name: "onSelectTab", type_: "String -> Effect Unit", description: "Tab selection callback" }
7275 ]
7376 ]
7477
@@ -109,21 +112,18 @@ 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: E.onString "itemId" 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 })" }
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+33−14
@@ -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,18 +86,22 @@ 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: E.onString "itemId" 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 })" }
85− , { name: "onSelectItem", type_: "EventHandler", description: "Item selection callback" }
104+ , { name: "onSelectItem", type_: "String -> Effect Unit", description: "Item selection callback" }
86105 ]
87106 , D.p [ DA.klass_ "text-sm text-gray-400 mt-2" ]
88107 [ D.text_ "The right-click target element is passed as the second argument (IsJSX)." ]
docs/src/Docs/Pages/RichMedia.purs+33−20
@@ -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: E.onNumber "latitude" setLat
3743 }"""
3844 [ propsTable
3945 [ { name: "latitude", type_: "Number", description: "Center latitude" }
@@ -43,38 +49,45 @@ mapView = L.componentDoc "nativeMapView" "Yoga.React.Native.MacOS.MapView (nativ
4349 , { name: "mapType", type_: "MapType", description: "Map display type" }
4450 , { name: "showsUserLocation", type_: "Boolean", description: "Show user location dot" }
4551 , { 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" }
52+ , { name: "onRegionChange", type_: "MapRegion -> Effect Unit", description: "Region change callback ({ latitude, longitude, latitudeDelta, longitudeDelta })" }
53+ , { name: "onSelectAnnotation", type_: "String -> Effect Unit", description: "Annotation tap callback" }
4854 ]
4955 ]
5056
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
57− , onPageChange: E.onInt "page" setPage
62+ , displayMode: T.singlePageContinuous
63+ , onPageChange: setPage
5864 }"""
5965 [ propsTable
6066 [ { name: "source", type_: "String", description: "PDF file path or URL" }
6167 , { name: "autoScales", type_: "Boolean", description: "Auto-fit to view" }
6268 , { name: "displayMode", type_: "PDFDisplayMode", description: "PDF display mode" }
63− , { name: "onPageChange", type_: "EventHandler", description: "Page change callback" }
69+ , { name: "onPageChange", type_: "Int -> Effect Unit", description: "Page change callback" }
6470 ]
6571 ]
6672
6773webView :: Nut
6874webView = L.componentDoc "nativeWebView" "Yoga.React.Native.MacOS.WebView (nativeWebView)"
69− """nativeWebView
70− { url: "https://purescript.org"
71− , onNavigate: E.onString "url" setUrl
72− , onFinishLoad: handler_ onPageLoaded
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" }
76− , { name: "onNavigate", type_: "EventHandler", description: "Navigation callback" }
77− , { name: "onFinishLoad", type_: "EventHandler", description: "Page load complete callback" }
89+ , { name: "onNavigate", type_: "String -> Effect Unit", description: "Navigation callback" }
90+ , { name: "onFinishLoad", type_: "String -> Effect Unit", description: "Page load complete callback" }
7891 ]
7992 ]
8093
docs/src/Docs/Pages/TextEditing.purs+12−12
@@ -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: E.onString "text" setText
23− , onSubmit: handler_ 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" }
@@ -28,23 +29,22 @@ textField = L.componentDoc "nativeTextField" "Yoga.React.Native.MacOS.TextField
2829 , { name: "secure", type_: "Boolean", description: "Password field (hides input)" }
2930 , { name: "search", type_: "Boolean", description: "Search field style" }
3031 , { 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)" }
32+ , { name: "onChangeText", type_: "String -> Effect Unit", description: "Text change callback" }
33+ , { name: "onSubmit", type_: "String -> Effect Unit", description: "Submit callback (Enter key)" }
3334 ]
3435 ]
3536
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: E.onString "text" setText
42+ , showsRuler: true
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/DataViews.purs+2−3
@@ -8,7 +8,6 @@ import React.Basic.Hooks (useState', (/\))
88import React.Basic.Hooks as React
99import Yoga.React (component)
1010import Yoga.React.Native (tw)
11−import Yoga.React.Native.MacOS.Events as E
1211import Yoga.React.Native.MacOS.OutlineView (OutlineItem(..), nativeOutlineView)
1312import Yoga.React.Native.MacOS.TableView (nativeTableView)
1413import Yoga.React.Native.Style as Style
@@ -35,8 +34,8 @@ tableViewDemo = component "TableViewDemo" \dp -> React.do
3534 ]
3635 , headerVisible: true
3736 , alternatingRows: true
38− , onSelectRow: E.onInt "rowIndex" \i -> setSelected ("Row " <> show i)
39− , onDoubleClickRow: E.onInt "rowIndex" \i -> setSelected ("Double-clicked row " <> show i)
37+ , onSelectRow: \i -> setSelected ("Row " <> show i)
38+ , onDoubleClickRow: \i -> setSelected ("Double-clicked row " <> show i)
4039 , style: Style.style { height: 180.0 } <> tw "rounded-lg overflow-hidden mb-2"
4140 }
4241 , if selected == "" then mempty
example-macos/src/Demo/DragDropFiles.purs+26−26
@@ -2,13 +2,14 @@ module Demo.DragDropFiles (dropZoneDemo, filePickerDemo) where
22
33import Prelude
44
5−import Demo.Shared (DemoProps, card, desc, getFieldJSON, label, scrollWrap, sectionTitle)
5+import Data.String (joinWith)
6+import Demo.Shared (DemoProps, card, desc, label, scrollWrap, sectionTitle)
67import React.Basic (JSX)
7−import React.Basic.Events (handler, handler_, unsafeEventFn)
88import React.Basic.Hooks (useState', (/\))
99import React.Basic.Hooks as React
1010import Yoga.React (component)
11−import Yoga.React.Native (nativeEvent, text, tw, view)
11+import Yoga.React.Native (text, tw, view)
12+import Yoga.React.Native.MacOS.DropZone (nativeDropZone)
1213import Yoga.React.Native.MacOS.FilePicker (nativeFilePicker)
1314import Yoga.React.Native.MacOS.Types as T
1415import Yoga.React.Native.Style as Style
@@ -23,18 +24,23 @@ dropZoneDemo = component "DropZoneDemo" \dp -> React.do
2324 scrollWrap dp
2425 [ sectionTitle dp.fg "Drop Zone"
2526 , desc dp "Drag files from Finder into the area below"
26− , view
27− { draggedTypes: [ "NSFilenamesPboardType" ]
28− , onDrop: handler
29− (nativeEvent >>> unsafeEventFn \e -> getFieldJSON "dataTransfer" e)
30− \r -> do
31− setDropped r
32− setStatus "Drop files here"
33− setDragging false
34− , onDragEnter: handler_ do
27+ , nativeDropZone
28+ { onFileDrop: \d -> do
29+ let fileNames = joinWith ", " (map _.name d.files)
30+ let stringItems = joinWith ", " d.strings
31+ let
32+ info = case fileNames, stringItems of
33+ "", "" -> "Empty drop"
34+ fs, "" -> "Files: " <> fs
35+ "", ss -> "Strings: " <> ss
36+ fs, ss -> "Files: " <> fs <> " | Strings: " <> ss
37+ setDropped info
38+ setStatus "Drop files here"
39+ setDragging false
40+ , onFilesDragEnter: do
3541 setStatus "Release to drop!"
3642 setDragging true
37− , onDragLeave: handler_ do
43+ , onFilesDragExit: do
3844 setStatus "Drop files here"
3945 setDragging false
4046 , style: tw "rounded-lg items-center justify-center mb-2"
@@ -48,7 +54,7 @@ dropZoneDemo = component "DropZoneDemo" \dp -> React.do
4854 [ text { style: tw "text-sm" <> Style.style { color: dp.fg } } status
4955 , if dropped == "" then mempty
5056 else text { style: tw "text-xs mt-2 px-4" <> Style.style { color: dp.dimFg } }
51− ("Dropped: " <> dropped)
57+ dropped
5258 ]
5359 ]
5460
@@ -68,10 +74,8 @@ filePickerDemo = component "FilePickerDemo" \dp -> React.do
6874 , allowMultiple: true
6975 , allowedTypes: [ "public.image", "public.text" ]
7076 , message: "Select files to open"
71− , onPickFiles: handler
72− (nativeEvent >>> unsafeEventFn \e -> getFieldJSON "files" e)
73− setPicked
74− , onCancel: handler_ (setPicked "Cancelled")
77+ , onPickFiles: \files -> setPicked (joinWith ", " (map _.path files))
78+ , onCancel: setPicked "Cancelled"
7579 , style: Style.style { height: 24.0, width: 120.0 }
7680 }
7781 , nativeFilePicker
@@ -80,10 +84,8 @@ filePickerDemo = component "FilePickerDemo" \dp -> React.do
8084 , sfSymbol: "folder"
8185 , canChooseDirectories: true
8286 , message: "Select a folder"
83− , onPickFiles: handler
84− (nativeEvent >>> unsafeEventFn \e -> getFieldJSON "files" e)
85− setPicked
86− , onCancel: handler_ (setPicked "Cancelled")
87+ , onPickFiles: \files -> setPicked (joinWith ", " (map _.path files))
88+ , onCancel: setPicked "Cancelled"
8789 , style: Style.style { height: 24.0, width: 140.0, marginLeft: 8.0 }
8890 }
8991 , nativeFilePicker
@@ -93,10 +95,8 @@ filePickerDemo = component "FilePickerDemo" \dp -> React.do
9395 , defaultName: "Untitled.txt"
9496 , allowedTypes: [ "public.plain-text" ]
9597 , message: "Choose save location"
96− , onPickFiles: handler
97− (nativeEvent >>> unsafeEventFn \e -> getFieldJSON "files" e)
98− setPicked
99− , onCancel: handler_ (setPicked "Cancelled")
98+ , onPickFiles: \files -> setPicked (joinWith ", " (map _.path files))
99+ , onCancel: setPicked "Cancelled"
100100 , style: Style.style { height: 24.0, width: 120.0, marginLeft: 8.0 }
101101 }
102102 ]
example-macos/src/Demo/InputControls.purs+17−17
@@ -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, 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)
@@ -180,7 +180,7 @@ stepperDemo = component "StepperDemo" \dp -> React.do
180180 , minValue: 0.0
181181 , maxValue: 50.0
182182 , increment: 1.0
183− , onChange: E.onNumber "value" setValue
183+ , onChange: setValue
184184 , style: Style.style { width: 100.0, height: 24.0 }
185185 }
186186 ]
@@ -240,14 +240,14 @@ checkboxDemo = component "CheckboxDemo" \dp -> React.do
240240 { checked: a
241241 , title: "Enable notifications"
242242 , enabled: true
243− , onChange: E.onBool "checked" setA
243+ , onChange: setA
244244 , style: Style.style { height: 24.0 } <> tw "mb-1"
245245 }
246246 , nativeCheckbox
247247 { checked: b
248248 , title: "Dark mode"
249249 , enabled: true
250− , onChange: E.onBool "checked" setB
250+ , onChange: setB
251251 , style: Style.style { height: 24.0 }
252252 }
253253 ]
@@ -264,21 +264,21 @@ radioButtonDemo = component "RadioButtonDemo" \dp -> React.do
264264 { selected: choice == "option1"
265265 , title: "Small"
266266 , enabled: true
267− , onChange: handler_ (setChoice "option1")
267+ , onChange: \_ -> setChoice "option1"
268268 , style: Style.style { height: 24.0 } <> tw "mb-1"
269269 }
270270 , nativeRadioButton
271271 { selected: choice == "option2"
272272 , title: "Medium"
273273 , enabled: true
274− , onChange: handler_ (setChoice "option2")
274+ , onChange: \_ -> setChoice "option2"
275275 , style: Style.style { height: 24.0 } <> tw "mb-1"
276276 }
277277 , nativeRadioButton
278278 { selected: choice == "option3"
279279 , title: "Large"
280280 , enabled: true
281− , onChange: handler_ (setChoice "option3")
281+ , onChange: \_ -> setChoice "option3"
282282 , style: Style.style { height: 24.0 }
283283 }
284284 , label dp.dimFg ("Selected: " <> choice)
example-macos/src/Demo/MapsDocs.purs+1−2
@@ -8,7 +8,6 @@ import React.Basic.Hooks (useState', (/\))
88import React.Basic.Hooks as React
99import Yoga.React (component)
1010import Yoga.React.Native (text, tw, view)
11−import Yoga.React.Native.MacOS.Events as E
1211import Yoga.React.Native.MacOS.MapView (nativeMapView)
1312import Yoga.React.Native.MacOS.PDFView (nativePDFView)
1413import Yoga.React.Native.MacOS.Segmented (nativeSegmented)
@@ -30,7 +29,7 @@ mapViewDemo = component "MapViewDemo" \dp -> React.do
3029 , nativeSegmented
3130 { labels: [ "Standard", "Satellite", "Hybrid" ]
3231 , selectedIndex: mapType
33− , onChange: E.onInt "selectedIndex" setMapType
32+ , onChange: setMapType
3433 , style: Style.style { width: 240.0, height: 24.0 }
3534 }
3635 ]
example-macos/src/Demo/Navigation.purs+4−1
@@ -6,6 +6,7 @@ import Data.Array (concatMap, null)
66import Data.String (toLower, contains, Pattern(..))
77import Demo.AiMl (cameraDemo, naturalLanguageDemo, ocrDemo, speechRecognitionDemo)
88import Demo.Animation (riveDemo)
9+import Demo.SpringAnimation (springDemo)
910import Demo.Chat (chatDemo)
1011import Demo.DataViews (outlineViewDemo, tableViewDemo)
1112import Demo.Display (animatedImageDemo, imageDemo, levelIndicatorDemo, pathControlDemo, progressDemo, separatorDemo, videoPlayerDemo)
@@ -80,7 +81,8 @@ outlineTree = do
8081 , leaf "filepicker" "File Picker" "folder"
8182 ]
8283 , folder "animation" "Animation" "play.circle"
83− [ leaf "rive" "Rive" "play.circle"
84+ [ leaf "springs" "Spring Physics" "sparkles"
85+ , leaf "rive" "Rive" "play.circle"
8486 ]
8587 , folder "system" "System Services" "gearshape.2"
8688 [ leaf "clipboard" "Clipboard" "doc.on.clipboard"
@@ -180,6 +182,7 @@ demoContent dp = case _ of
180182 "outlineview" -> outlineViewDemo dp
181183 "dropzone" -> dropZoneDemo dp
182184 "filepicker" -> filePickerDemo dp
185+ "springs" -> springDemo dp
183186 "rive" -> riveDemo dp
184187 "clipboard" -> clipboardDemo dp
185188 "share" -> shareDemo dp
example-macos/src/Demo/SpringAnimation.pursnew file+123
@@ -0,0 +1,123 @@
1+module Demo.SpringAnimation (springDemo) where
2+
3+import Prelude
4+
5+import Demo.Shared (DemoProps, card, desc, scrollWrap, sectionTitle)
6+import React.Basic (JSX)
7+import React.Basic.Hooks (useState', (/\))
8+import React.Basic.Hooks as React
9+import Yoga.React (component)
10+import Yoga.React.Native (text, tw, view)
11+import Yoga.React.Native.Animated (animatedView, animatedView_, interpolate, useSpring)
12+import React.Basic.Events (handler_)
13+import Yoga.React.Native.Pressable (pressable)
14+import Yoga.React.Native.MacOS.Button (nativeButton)
15+import Yoga.React.Native.MacOS.Types as T
16+import Yoga.React.Native.Style as Style
17+
18+springDemo :: DemoProps -> JSX
19+springDemo = component "SpringDemo" \dp -> React.do
20+ pressed /\ setPressed <- useState' false
21+ toggled /\ setToggled <- useState' false
22+ fadeIn /\ setFadeIn <- useState' false
23+ showItems /\ setShowItems <- useState' false
24+ let scaleTarget = if pressed then 0.92 else 1.0
25+ scale <- useSpring scaleTarget { stiffness: 400.0, damping: 15.0 }
26+ let toggleTarget = if toggled then 200.0 else 0.0
27+ translateX <- useSpring toggleTarget { stiffness: 180.0, damping: 14.0 }
28+ let fadeTarget = if fadeIn then 1.0 else 0.0
29+ opacity <- useSpring fadeTarget { stiffness: 120.0, damping: 20.0 }
30+ item0 <- useSpring (if showItems then 1.0 else 0.0) { stiffness: 200.0, damping: 18.0 }
31+ item1 <- useSpring (if showItems then 1.0 else 0.0) { stiffness: 180.0, damping: 18.0, delay: 50 }
32+ item2 <- useSpring (if showItems then 1.0 else 0.0) { stiffness: 160.0, damping: 18.0, delay: 100 }
33+ item3 <- useSpring (if showItems then 1.0 else 0.0) { stiffness: 140.0, damping: 18.0, delay: 150 }
34+ pure do
35+ scrollWrap dp
36+ [ sectionTitle dp.fg "Spring Animations"
37+ , desc dp "Declarative spring physics using useSpring"
38+
39+ , sectionTitle dp.fg "Scale on Press"
40+ , desc dp "Hold down the card — it springs to 0.92x, release to bounce back"
41+ , pressable
42+ { onPressIn: handler_ (setPressed true)
43+ , onPressOut: handler_ (setPressed false)
44+ }
45+ [ animatedView
46+ { style: Style.style { transform: [ { scale } ] } }
47+ [ card dp.cardBg
48+ [ view { style: tw "items-center py-4" }
49+ [ text { style: tw "text-sm font-semibold" <> Style.style { color: dp.fg } }
50+ (if pressed then "Pressing..." else "Hold me!")
51+ , text { style: tw "text-xs mt-1" <> Style.style { color: dp.dimFg } }
52+ "useSpring { stiffness: 400, damping: 15 }"
53+ ]
54+ ]
55+ ]
56+ ]
57+
58+ , sectionTitle dp.fg "Spring Toggle"
59+ , desc dp "Slides a box 200px to the right with a spring"
60+ , nativeButton
61+ { title: if toggled then "Spring Left" else "Spring Right"
62+ , bezelStyle: T.push
63+ , onPress: setToggled (not toggled)
64+ , style: Style.style { height: 24.0, width: 140.0, marginBottom: 8.0 }
65+ }
66+ , animatedView_
67+ { style: tw "rounded-lg"
68+ <> Style.style
69+ { width: 60.0
70+ , height: 60.0
71+ , backgroundColor: "#007AFF"
72+ , transform: [ { translateX } ]
73+ }
74+ }
75+
76+ , sectionTitle dp.fg "Fade In"
77+ , desc dp "Springs opacity from 0 to 1"
78+ , nativeButton
79+ { title: if fadeIn then "Fade Out" else "Fade In"
80+ , bezelStyle: T.push
81+ , onPress: setFadeIn (not fadeIn)
82+ , style: Style.style { height: 24.0, width: 120.0, marginBottom: 8.0 }
83+ }
84+ , animatedView
85+ { style: Style.style { opacity } }
86+ [ card dp.cardBg
87+ [ view { style: tw "items-center py-3" }
88+ [ text { style: tw "text-sm" <> Style.style { color: dp.fg } } "I fade with a spring curve"
89+ ]
90+ ]
91+ ]
92+
93+ , sectionTitle dp.fg "Staggered Items"
94+ , desc dp "Each item springs in with increasing delay"
95+ , nativeButton
96+ { title: if showItems then "Hide Items" else "Show Items"
97+ , bezelStyle: T.push
98+ , onPress: setShowItems (not showItems)
99+ , style: Style.style { height: 24.0, width: 120.0, marginBottom: 8.0 }
100+ }
101+ , staggeredItem dp "PureScript" "#007AFF" item0
102+ , staggeredItem dp "React Native" "#34C759" item1
103+ , staggeredItem dp "macOS" "#FF9500" item2
104+ , staggeredItem dp "Spring Physics" "#AF52DE" item3
105+ ]
106+ where
107+ staggeredItem _ title color anim =
108+ animatedView
109+ { style: Style.style
110+ { opacity: anim
111+ , transform: [ { translateX: slideX } ]
112+ , marginBottom: 4.0
113+ }
114+ }
115+ [ view
116+ { style: tw "rounded-lg px-4 py-2 flex-row items-center"
117+ <> Style.style { backgroundColor: color }
118+ }
119+ [ text { style: tw "text-sm font-semibold" <> Style.style { color: "#FFFFFF" } } title
120+ ]
121+ ]
122+ where
123+ slideX = interpolate anim { inputRange: [ 0.0, 1.0 ], outputRange: [ -30.0, 0.0 ], extrapolate: "clamp" }
spago.lock+10−1
@@ -17,6 +17,12 @@
1717 {
1818 "functions": ">=6.0.0 <7.0.0"
1919 },
20+ {
21+ "maybe": ">=6.0.0 <7.0.0"
22+ },
23+ {
24+ "newtype": ">=5.0.0 <6.0.0"
25+ },
2026 {
2127 "nullable": ">=6.0.0 <7.0.0"
2228 },
@@ -27,7 +33,10 @@
2733 "react-basic": ">=17.0.0 <18.0.0"
2834 },
2935 {
30− "react-basic-hooks": ">=8.2.0 <10.0.0"
36+ "react-basic-hooks": ">=8.2.0 <9.0.0"
37+ },
38+ {
39+ "tuples": ">=7.0.0 <8.0.0"
3140 },
3241 {
3342 "type-equality": ">=4.0.1 <5.0.0"
spago.yaml+5−2
@@ -1,7 +1,7 @@
11package:
22 name: yoga-react-native
33 publish:
4− version: 0.1.0
4+ version: 0.2.1
55 license: MIT
66 location:
77 githubOwner: rowtype-yoga
@@ -11,10 +11,13 @@ package:
1111 - effect: ">=4.0.0 <5.0.0"
1212 - foreign: ">=7.0.0 <8.0.0"
1313 - functions: ">=6.0.0 <7.0.0"
14+ - maybe: ">=6.0.0 <7.0.0"
15+ - newtype: ">=5.0.0 <6.0.0"
1416 - nullable: ">=6.0.0 <7.0.0"
1517 - prelude: ">=6.0.1 <7.0.0"
1618 - react-basic: ">=17.0.0 <18.0.0"
17− - react-basic-hooks: ">=8.2.0 <10.0.0"
19+ - react-basic-hooks: ">=8.2.0 <9.0.0"
20+ - tuples: ">=7.0.0 <8.0.0"
1821 - type-equality: ">=4.0.1 <5.0.0"
1922 - typelevel-prelude: ">=7.0.0 <8.0.0"
2023 - unsafe-coerce: ">=6.0.0 <7.0.0"
src/Yoga/React/Native/Animated.js+24−1
@@ -1,4 +1,5 @@
11import { Animated, useAnimatedValue } from "react-native";
2+import { useRef, useEffect } from "react";
23
34export const newValueImpl = (n) => new Animated.Value(n);
45export const newValueXYImpl = (x, y) => new Animated.ValueXY({ x, y });
@@ -27,9 +28,31 @@ export const subtractImpl = (a) => (b) => Animated.subtract(a, b);
2728export const multiplyImpl = (a) => (b) => Animated.multiply(a, b);
2829export const divideImpl = (a) => (b) => Animated.divide(a, b);
2930export const moduloImpl = (a) => (n) => Animated.modulo(a, n);
30−export const diffClampImpl = (a) => (min) => (max) => Animated.diffClamp(a, min, max);
31+export const diffClampImpl = (a) => (min) => (max) =>
32+ Animated.diffClamp(a, min, max);
3133export const _animatedViewImpl = Animated.View;
3234export const _animatedTextImpl = Animated.Text;
3335export const _animatedImageImpl = Animated.Image;
3436export const _animatedScrollViewImpl = Animated.ScrollView;
3537export const useAnimatedValueImpl = (n) => () => useAnimatedValue(n);
38+
39+export const useSpringImpl = (target) => (config) => () => {
40+ const valueRef = useRef(null);
41+ const prevTargetRef = useRef(target);
42+ if (valueRef.current === null) {
43+ valueRef.current = new Animated.Value(target);
44+ }
45+ useEffect(() => {
46+ if (prevTargetRef.current !== target) {
47+ prevTargetRef.current = target;
48+ const anim = Animated.spring(valueRef.current, {
49+ toValue: target,
50+ useNativeDriver: false,
51+ ...config,
52+ });
53+ anim.start();
54+ return () => anim.stop();
55+ }
56+ });
57+ return valueRef.current;
58+};
src/Yoga/React/Native/Animated.purs+9
@@ -39,6 +39,8 @@ module Yoga.React.Native.Animated
3939 , animatedScrollView_
4040 , useAnimatedValue
4141 , UseAnimatedValue
42+ , useSpring
43+ , UseSpring
4244 , TimingConfig
4345 , SpringConfig
4446 , DecayConfig
@@ -272,3 +274,10 @@ foreign import data UseAnimatedValue :: Type -> Type
272274
273275useAnimatedValue :: Number -> Hook UseAnimatedValue AnimatedValue
274276useAnimatedValue n = unsafeHook (useAnimatedValueImpl n)
277+
278+foreign import useSpringImpl :: forall r. Number -> { | r } -> Effect AnimatedValue
279+
280+foreign import data UseSpring :: Type -> Type
281+
282+useSpring :: forall r. Number -> { | r } -> Hook UseSpring AnimatedValue
283+useSpring target config = unsafeHook (useSpringImpl target config)
src/Yoga/React/Native/MacOS/Checkbox.js+9−1
@@ -1,2 +1,10 @@
1+import React from "react";
12import { requireNativeComponent } from "react-native";
2−export const checkboxImpl = requireNativeComponent("MacOSCheckbox");
3+const NativeCheckbox = requireNativeComponent("MacOSCheckbox");
4+export const checkboxImpl = React.forwardRef((props, ref) => {
5+ const nativeProps = { ...props };
6+ if (typeof props.onChange === "function") {
7+ nativeProps.onChange = (e) => props.onChange(!!e.nativeEvent.checked)();
8+ }
9+ return React.createElement(NativeCheckbox, { ...nativeProps, ref });
10+});
src/Yoga/React/Native/MacOS/Checkbox.purs+3−2
@@ -3,8 +3,9 @@ module Yoga.React.Native.MacOS.Checkbox
33 , NativeCheckboxAttributes
44 ) where
55
6+import Prelude
7+import Effect (Effect)
68import React.Basic (ReactComponent)
7−import React.Basic.Events (EventHandler)
89import Yoga.React.Native.Attributes (BaseAttributes)
910import Yoga.React.Native.Internal (FFINativeComponent_, createNativeElement_)
1011
@@ -17,5 +18,5 @@ type NativeCheckboxAttributes = BaseAttributes
1718 ( checked :: Boolean
1819 , title :: String
1920 , enabled :: Boolean
20− , onChange :: EventHandler
21+ , onChange :: Boolean -> Effect Unit
2122 )
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/PDFView.js+10−1
@@ -1,2 +1,11 @@
1+import React from "react";
12import { requireNativeComponent } from "react-native";
2−export const pdfViewImpl = requireNativeComponent("MacOSPDFView");
3+const NativePDFView = requireNativeComponent("MacOSPDFView");
4+export const pdfViewImpl = React.forwardRef((props, ref) => {
5+ const nativeProps = { ...props };
6+ if (typeof props.onPageChange === "function") {
7+ nativeProps.onPageChange = (e) =>
8+ props.onPageChange(e.nativeEvent.pageIndex | 0)();
9+ }
10+ return React.createElement(NativePDFView, { ...nativeProps, ref });
11+});
src/Yoga/React/Native/MacOS/PDFView.purs+3−2
@@ -3,8 +3,9 @@ module Yoga.React.Native.MacOS.PDFView
33 , NativePDFViewAttributes
44 ) where
55
6+import Prelude
7+import Effect (Effect)
68import React.Basic (ReactComponent)
7−import React.Basic.Events (EventHandler)
89import Yoga.React.Native.Attributes (BaseAttributes)
910import Yoga.React.Native.Internal (FFINativeComponent_, createNativeElement_)
1011import Yoga.React.Native.MacOS.Types (PDFDisplayMode)
@@ -18,5 +19,5 @@ type NativePDFViewAttributes = BaseAttributes
1819 ( source :: String
1920 , autoScales :: Boolean
2021 , displayMode :: PDFDisplayMode
21− , onPageChange :: EventHandler
22+ , onPageChange :: Int -> Effect Unit
2223 )
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 )
src/Yoga/React/Native/MacOS/RadioButton.js+9−1
@@ -1,2 +1,10 @@
1+import React from "react";
12import { requireNativeComponent } from "react-native";
2−export const radioButtonImpl = requireNativeComponent("MacOSRadioButton");
3+const NativeRadioButton = requireNativeComponent("MacOSRadioButton");
4+export const radioButtonImpl = React.forwardRef((props, ref) => {
5+ const nativeProps = { ...props };
6+ if (typeof props.onChange === "function") {
7+ nativeProps.onChange = (e) => props.onChange(!!e.nativeEvent.selected)();
8+ }
9+ return React.createElement(NativeRadioButton, { ...nativeProps, ref });
10+});
src/Yoga/React/Native/MacOS/RadioButton.purs+3−2
@@ -3,8 +3,9 @@ module Yoga.React.Native.MacOS.RadioButton
33 , NativeRadioButtonAttributes
44 ) where
55
6+import Prelude
7+import Effect (Effect)
68import React.Basic (ReactComponent)
7−import React.Basic.Events (EventHandler)
89import Yoga.React.Native.Attributes (BaseAttributes)
910import Yoga.React.Native.Internal (FFINativeComponent_, createNativeElement_)
1011
@@ -17,5 +18,5 @@ type NativeRadioButtonAttributes = BaseAttributes
1718 ( selected :: Boolean
1819 , title :: String
1920 , enabled :: Boolean
20− , onChange :: EventHandler
21+ , onChange :: Boolean -> Effect Unit
2122 )
src/Yoga/React/Native/MacOS/Segmented.js+10−1
@@ -1,2 +1,11 @@
1+import React from "react";
12import { requireNativeComponent } from "react-native";
2−export const segmentedImpl = requireNativeComponent("NativeSegmented");
3+const NativeSegmented = requireNativeComponent("NativeSegmented");
4+export const segmentedImpl = 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(NativeSegmented, { ...nativeProps, ref });
11+});
src/Yoga/React/Native/MacOS/Segmented.purs+3−2
@@ -1,7 +1,8 @@
11module Yoga.React.Native.MacOS.Segmented (nativeSegmented, NativeSegmentedAttributes) 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
@@ -14,5 +15,5 @@ type NativeSegmentedAttributes = BaseAttributes
1415 ( labels :: Array String
1516 , sfSymbols :: Array String
1617 , selectedIndex :: Int
17− , onChange :: EventHandler
18+ , onChange :: Int -> Effect Unit
1819 )
src/Yoga/React/Native/MacOS/Stepper.js+9−1
@@ -1,2 +1,10 @@
1+import React from "react";
12import { requireNativeComponent } from "react-native";
2−export const stepperImpl = requireNativeComponent("MacOSStepper");
3+const NativeStepper = requireNativeComponent("MacOSStepper");
4+export const stepperImpl = React.forwardRef((props, ref) => {
5+ const nativeProps = { ...props };
6+ if (typeof props.onChange === "function") {
7+ nativeProps.onChange = (e) => props.onChange(e.nativeEvent.value ?? 0)();
8+ }
9+ return React.createElement(NativeStepper, { ...nativeProps, ref });
10+});
src/Yoga/React/Native/MacOS/Stepper.purs+3−2
@@ -3,8 +3,9 @@ module Yoga.React.Native.MacOS.Stepper
33 , NativeStepperAttributes
44 ) where
55
6+import Prelude
7+import Effect (Effect)
68import React.Basic (ReactComponent)
7−import React.Basic.Events (EventHandler)
89import Yoga.React.Native.Attributes (BaseAttributes)
910import Yoga.React.Native.Internal (FFINativeComponent_, createNativeElement_)
1011
@@ -18,5 +19,5 @@ type NativeStepperAttributes = BaseAttributes
1819 , minValue :: Number
1920 , maxValue :: Number
2021 , increment :: Number
21− , onChange :: EventHandler
22+ , onChange :: Number -> Effect Unit
2223 )
src/Yoga/React/Native/MacOS/TableView.js+14−1
@@ -1,2 +1,15 @@
1+import React from "react";
12import { requireNativeComponent } from "react-native";
2−export const tableViewImpl = requireNativeComponent("MacOSTableView");
3+const NativeTableView = requireNativeComponent("MacOSTableView");
4+export const tableViewImpl = React.forwardRef((props, ref) => {
5+ const nativeProps = { ...props };
6+ if (typeof props.onSelectRow === "function") {
7+ nativeProps.onSelectRow = (e) =>
8+ props.onSelectRow(e.nativeEvent.rowIndex | 0)();
9+ }
10+ if (typeof props.onDoubleClickRow === "function") {
11+ nativeProps.onDoubleClickRow = (e) =>
12+ props.onDoubleClickRow(e.nativeEvent.rowIndex | 0)();
13+ }
14+ return React.createElement(NativeTableView, { ...nativeProps, ref });
15+});
src/Yoga/React/Native/MacOS/TableView.purs+4−3
@@ -4,8 +4,9 @@ module Yoga.React.Native.MacOS.TableView
44 , TableColumn
55 ) where
66
7+import Prelude
8+import Effect (Effect)
79import React.Basic (ReactComponent)
8−import React.Basic.Events (EventHandler)
910import Yoga.React.Native.Attributes (BaseAttributes)
1011import Yoga.React.Native.Internal (FFINativeComponent_, createNativeElement_)
1112
@@ -25,6 +26,6 @@ type NativeTableViewAttributes = BaseAttributes
2526 , rows :: Array (Array String)
2627 , headerVisible :: Boolean
2728 , alternatingRows :: Boolean
28− , onSelectRow :: EventHandler
29− , onDoubleClickRow :: EventHandler
29+ , onSelectRow :: Int -> Effect Unit
30+ , onDoubleClickRow :: Int -> Effect Unit
3031 )