diff --git a/src/React.bs.js b/src/React.bs.js index c2471b6..ef827dd 100644 --- a/src/React.bs.js +++ b/src/React.bs.js @@ -3,8 +3,6 @@ var React = require("react"); -var Ref = {}; - var Children = {}; var Context = {}; @@ -25,7 +23,6 @@ function lazy_(load) { var Uncurried = {}; -exports.Ref = Ref; exports.Children = Children; exports.Context = Context; exports.Fragment = Fragment; diff --git a/src/React.res b/src/React.res index 0beaa59..f1f6831 100644 --- a/src/React.res +++ b/src/React.res @@ -5,6 +5,7 @@ type element = Jsx.element external float: float => element = "%identity" external int: int => element = "%identity" external string: string => element = "%identity" +external promise: promise => element = "%identity" external array: array => element = "%identity" @@ -250,6 +251,9 @@ external useCallback7: ('callback, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => 'callback = @module("react") external useContext: Context.t<'any> => 'any = "useContext" +@module("react") +external usePromise: promise<'a> => 'a = "use" + @module("react") external useRef: 'value => ref<'value> = "useRef" @module("react") @@ -309,10 +313,9 @@ external useImperativeHandle7: ( @module("react") external useId: unit => string = "useId" -@module("react") external useDeferredValue: 'value => 'value = "useDeferredValue" - +/** `useDeferredValue` is a React Hook that lets you defer updating a part of the UI. */ @module("react") -external useTransition: unit => (bool, (unit => unit) => unit) = "useTransition" +external useDeferredValue: ('value, ~initialValue: 'value=?) => 'value = "useDeferredValue" @module("react") external useInsertionEffectOnEveryRender: (unit => option unit>) => unit = @@ -405,3 +408,36 @@ external setDisplayName: (component<'props>, string) => unit = "displayName" @get @return(nullable) external displayName: component<'props> => option = "displayName" + +// Actions + +type transitionFunction = unit => promise + +type transitionStartFunction = transitionFunction => unit + +/** `useTransition` is a React Hook that lets you render a part of the UI in the background. */ +@module("react") +external useTransition: unit => (bool, transitionStartFunction) = "useTransition" + +type action<'state, 'payload> = ('state, 'payload) => promise<'state> + +type formAction<'formData> = 'formData => promise + +/** `useActionState` is a Hook that allows you to update state based on the result of a form action. */ +@module("react") +external useActionState: ( + action<'state, 'payload>, + 'state, + ~permalink: string=?, +) => ('state, formAction<'payload>, bool) = "useActionState" + +/** `useOptimistic` is a React Hook that lets you optimistically update the UI. */ +@module("react") +external useOptimistic: ( + 'state, + ~updateFn: ('state, 'action) => 'state=?, +) => ('state, 'action => unit) = "useOptimistic" + +/** `act` is a test helper to apply pending React updates before making assertions. */ +@module("react") +external act: (unit => promise) => promise = "act" diff --git a/src/ReactDOM.bs.js b/src/ReactDOM.bs.js index b65084d..fa51640 100644 --- a/src/ReactDOM.bs.js +++ b/src/ReactDOM.bs.js @@ -1,6 +1,7 @@ // Generated by ReScript, PLEASE EDIT WITH CARE 'use strict'; +var Caml_option = require("rescript/lib/js/caml_option.js"); var Root = {}; @@ -8,14 +9,50 @@ var Client = { Root: Root }; -var Ref = {}; +function getString(formData, name) { + var value = formData.get(name); + if (!(value == null) && typeof value === "string") { + return Caml_option.some(value); + } + +} + +function getFile(formData, name) { + var value = formData.get(name); + if (!(value == null) && typeof value !== "string") { + return Caml_option.some(value); + } + +} + +function getAll(t, string) { + return t.getAll(string).map(function (value) { + if (typeof value === "string") { + return { + TAG: "String", + _0: value + }; + } else { + return { + TAG: "File", + _0: value + }; + } + }); +} -var Props = {}; +var $$FormData = { + getString: getString, + getFile: getFile, + getAll: getAll +}; + +var Ref = {}; var Style; exports.Client = Client; +exports.$$FormData = $$FormData; exports.Ref = Ref; -exports.Props = Props; exports.Style = Style; /* No side effect */ diff --git a/src/ReactDOM.res b/src/ReactDOM.res index 04c39c2..fbd564b 100644 --- a/src/ReactDOM.res +++ b/src/ReactDOM.res @@ -25,6 +25,52 @@ module Client = { external hydrateRoot: (Dom.element, React.element) => Root.t = "hydrateRoot" } +// Very rudimentary form data bindings +module FormData = { + type t + type file + + type formValue = + | String(string) + | File(file) + + @new external make: unit => t = "FormData" + + @send external append: (t, string, ~filename: string=?) => unit = "append" + @send external delete: (t, string) => unit = "delete" + @return(nullable) @send external getUnsafe: (t, string) => option<'a> = "get" + @send external getAllUnsafe: (t, string) => array<'a> = "getAll" + + let getString = (formData, name) => { + switch formData->getUnsafe(name) { + | Some(value) => Js.typeof(value) === "string" ? Some(value) : None + | _ => None + } + } + + external _asFile: 'a => file = "%identity" + + let getFile = (formData, name) => { + switch formData->getUnsafe(name) { + | Some(value) => Js.typeof(value) === "string" ? None : Some(value->_asFile) + | _ => None + } + } + + let getAll = (t, string) => { + t + ->getAllUnsafe(string) + ->Js.Array2.map(value => { + Js.typeof(value) === "string" ? String(value) : File(value->_asFile) + }) + } + + @send external set: (string, string) => unit = "set" + @send external has: string => bool = "has" + // @send external keys: t => Iterator.t = "keys"; + // @send external values: t => Iterator.t = "values"; +} + @module("react-dom") external createPortal: (React.element, Dom.element) => React.element = "createPortal" @@ -37,12 +83,142 @@ type domRef = JsxDOM.domRef module Ref = { type t = domRef type currentDomRef = React.ref> - type callbackDomRef = Js.nullable => unit + type callbackDomRef = Js.nullable => option unit> external domRef: currentDomRef => domRef = "%identity" external callbackDomRef: callbackDomRef => domRef = "%identity" } +// Hooks + +type formStatus<'state> = { + /** If true, this means the parent
is pending submission. Otherwise, false. */ + pending: bool, + /** An object implementing the FormData interface that contains the data the parent is submitting. If there is no active submission or no parent , it will be null. */ + data: FormData.t, + /** This represents whether the parent is submitting with either a GET or POST HTTP method. By default, a will use the GET method and can be specified by the method property. */ + method: [#get | #post], + /** A reference to the function passed to the action prop on the parent . If there is no parent , the property is null. If there is a URI value provided to the action prop, or no action prop specified, status.action will be null. */ + action: React.action<'state, FormData.t>, +} + +external formAction: React.formAction => string = "%identity" + +/** `useFormStatus` is a Hook that gives you status information of the last form submission. */ +@module("react-dom") +external useFormStatus: unit => formStatus<'state> = "useFormStatus" + +// Resource Preloading APIs + +/** The CORS policy to use. */ +type crossOrigin = [ + | #anonymous + | #"use-credentials" +] + +/** The Referrer header to send when fetching. */ +type referrerPolicy = [ + | #"referrer-when-downgrade" + | #"no-referrer" + | #origin + | #"origin-when-cross-origin" + | #"unsafe-url" +] + +/** Suggests a relative priority for fetching the resource. */ +type fetchPriority = [#auto | #high | #low] + +/** `prefetchDNS` lets you eagerly look up the IP of a server that you expect to load resources from. */ +@module("react-dom") +external prefetchDNS: string => unit = "prefetchDNS" + +/** `preconnect` lets you eagerly connect to a server that you expect to load resources from. */ +@module("react-dom") +external preconnect: string => unit = "preconnect" + +type preloadOptions = { + /** The type of resource. */ + @as("as") + as_: [ + | #audio + | #document + | #embed + | #fetch + | #font + | #image + | #object + | #script + | #style + | #track + | #video + | #worker + ], + /** The CORS policy to use. It is required when as is set to "fetch". */ + crossOrigin?: crossOrigin, + /** The Referrer header to send when fetching. */ + referrerPolicy?: referrerPolicy, + /** A cryptographic hash of the resource, to verify its authenticity. */ + integrity?: string, + /** The MIME type of the resource. */ + @as("type") + type_?: string, + /** A cryptographic nonce to allow the resource when using a strict Content Security Policy. */ + nonce?: string, + /** Suggests a relative priority for fetching the resource. */ + fetchPriority?: fetchPriority, + /** For use only with as: "image". Specifies the source set of the image. */ + imageSrcSet?: string, + /** For use only with as: "image". Specifies the sizes of the image. */ + imageSizes?: string, +} + +/** `preload` lets you eagerly fetch a resource such as a stylesheet, font, or external script that you expect to use. */ +@module("react-dom") +external preload: (string, preloadOptions) => unit = "preload" + +type preloadModuleOptions = { + /** The type of resource. */ + @as("as") + as_: [#script], + /** The CORS policy to use. It is required when as is set to "fetch". */ + crossOrigin?: crossOrigin, + /** A cryptographic hash of the resource, to verify its authenticity. */ + integrity?: string, + /** A cryptographic nonce to allow the resource when using a strict Content Security Policy. */ + nonce?: string, +} + +/** `preloadModule` lets you eagerly fetch an ESM module that you expect to use. */ +@module("react-dom") +external preloadModule: (string, preloadModuleOptions) => unit = "preloadModule" + +type preinitOptions = { + /** The type of resource. */ + @as("as") + as_: [#script | #style], + /** Required with stylesheets. Says where to insert the stylesheet relative to others. Stylesheets with higher precedence can override those with lower precedence. */ + precedence?: [#reset | #low | #medium | #high], + /** The CORS policy to use. It is required when as is set to "fetch". */ + crossOrigin?: crossOrigin, + /** The Referrer header to send when fetching. */ + referrerPolicy?: referrerPolicy, + /** A cryptographic hash of the resource, to verify its authenticity. */ + integrity?: string, + nonce?: string, + /** Suggests a relative priority for fetching the resource. */ + fetchPriority?: fetchPriority, +} + +/** `preinit` lets you eagerly fetch and evaluate a stylesheet or external script. */ +@module("react-dom") +external preinit: (string, preinitOptions) => unit = "preinit" + +/** To preinit an ESM module, call the `preinitModule` function from react-dom. */ +@module("react-dom") +external preinitModule: (string, preloadModuleOptions) => unit = "preinitModule" + +// Runtime + type domProps = JsxDOM.domProps @variadic @module("react") diff --git a/src/ReactDOMStatic.bs.js b/src/ReactDOMStatic.bs.js new file mode 100644 index 0000000..d856702 --- /dev/null +++ b/src/ReactDOMStatic.bs.js @@ -0,0 +1,2 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE +/* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */ diff --git a/src/ReactDOMStatic.res b/src/ReactDOMStatic.res new file mode 100644 index 0000000..7988fbb --- /dev/null +++ b/src/ReactDOMStatic.res @@ -0,0 +1,30 @@ +type abortSignal // WebAPI.EventAPI.abortSignal + +type nodeStream // NodeJs.Stream.stream + +type readableStream // WebAPI.FileAPI.readableStream + +type prerenderOptions<'error> = { + bootstrapScriptContent?: string, + bootstrapScripts?: array, + bootstrapModules?: array, + identifierPrefix?: string, + namespaceURI?: string, + onError?: 'error => unit, + progressiveChunkSize?: int, + signal?: abortSignal, +} + +type staticResult = {prelude: readableStream} + +@module("react-dom/static") +external prerender: (React.element, ~options: prerenderOptions<'error>=?) => promise = + "prerender" + +type staticResultNode = {prelude: nodeStream} + +@module("react-dom/static") +external prerenderToNodeStream: ( + React.element, + ~options: prerenderOptions<'error>=?, +) => promise = "prerenderToNodeStream"