@bind-ts/react-bind 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +22 -0
- package/dist/esm/createBindHook.d.ts +112 -0
- package/dist/esm/createBindHook.d.ts.map +1 -0
- package/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +388 -0
- package/dist/esm/types.d.ts +46 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/useBind.d.ts +49 -0
- package/dist/esm/useBind.d.ts.map +1 -0
- package/dist/esm/useIsomorphicLayoutEffect.d.ts +6 -0
- package/dist/esm/useIsomorphicLayoutEffect.d.ts.map +1 -0
- package/package.json +57 -0
- package/src/createBindHook.tsx +241 -0
- package/src/index.ts +16 -0
- package/src/types.ts +50 -0
- package/src/useBind.tsx +144 -0
- package/src/useIsomorphicLayoutEffect.ts +7 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("react"),oe=require("@bind/core"),W=require("@tanstack/react-store");var h={exports:{}},R={};/**
|
|
2
|
+
* @license React
|
|
3
|
+
* react-jsx-runtime.production.js
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
6
|
+
*
|
|
7
|
+
* This source code is licensed under the MIT license found in the
|
|
8
|
+
* LICENSE file in the root directory of this source tree.
|
|
9
|
+
*/var M;function ae(){if(M)return R;M=1;var a=Symbol.for("react.transitional.element"),n=Symbol.for("react.fragment");function l(i,t,u){var f=null;if(u!==void 0&&(f=""+u),t.key!==void 0&&(f=""+t.key),"key"in t){u={};for(var E in t)E!=="key"&&(u[E]=t[E])}else u=t;return t=u.ref,{$$typeof:a,type:i,key:f,ref:t!==void 0?t:null,props:u}}return R.Fragment=n,R.jsx=l,R.jsxs=l,R}var x={};/**
|
|
10
|
+
* @license React
|
|
11
|
+
* react-jsx-runtime.development.js
|
|
12
|
+
*
|
|
13
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
14
|
+
*
|
|
15
|
+
* This source code is licensed under the MIT license found in the
|
|
16
|
+
* LICENSE file in the root directory of this source tree.
|
|
17
|
+
*/var L;function ue(){return L||(L=1,process.env.NODE_ENV!=="production"&&(function(){function a(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===re?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case j:return"Fragment";case z:return"Profiler";case J:return"StrictMode";case Z:return"Suspense";case Q:return"SuspenseList";case ee:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case V:return"Portal";case H:return e.displayName||"Context";case G:return(e._context.displayName||"Context")+".Consumer";case X:var r=e.render;return e=e.displayName,e||(e=r.displayName||r.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case K:return r=e.displayName||null,r!==null?r:a(e.type)||"Memo";case y:r=e._payload,e=e._init;try{return a(e(r))}catch{}}return null}function n(e){return""+e}function l(e){try{n(e);var r=!1}catch{r=!0}if(r){r=console;var o=r.error,s=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return o.call(r,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",s),n(e)}}function i(e){if(e===j)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===y)return"<...>";try{var r=a(e);return r?"<"+r+">":"<...>"}catch{return"<...>"}}function t(){var e=O.A;return e===null?null:e.getOwner()}function u(){return Error("react-stack-top-frame")}function f(e){if(N.call(e,"key")){var r=Object.getOwnPropertyDescriptor(e,"key").get;if(r&&r.isReactWarning)return!1}return e.key!==void 0}function E(e,r){function o(){Y||(Y=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",r))}o.isReactWarning=!0,Object.defineProperty(e,"key",{get:o,configurable:!0})}function T(){var e=a(this.type);return F[e]||(F[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function k(e,r,o,s,S,g){var c=o.ref;return e={$$typeof:A,type:e,key:r,props:o,_owner:s},(c!==void 0?c:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:T}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:S}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:g}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function w(e,r,o,s,S,g){var c=r.children;if(c!==void 0)if(s)if(te(c)){for(s=0;s<c.length;s++)p(c[s]);Object.freeze&&Object.freeze(c)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else p(c);if(N.call(r,"key")){c=a(e);var _=Object.keys(r).filter(function(ne){return ne!=="key"});s=0<_.length?"{key: someKey, "+_.join(": ..., ")+": ...}":"{key: someKey}",I[c+s]||(_=0<_.length?"{"+_.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
|
|
18
|
+
let props = %s;
|
|
19
|
+
<%s {...props} />
|
|
20
|
+
React keys must be passed directly to JSX without using spread:
|
|
21
|
+
let props = %s;
|
|
22
|
+
<%s key={someKey} {...props} />`,s,c,_,c),I[c+s]=!0)}if(c=null,o!==void 0&&(l(o),c=""+o),f(r)&&(l(r.key),c=""+r.key),"key"in r){o={};for(var C in r)C!=="key"&&(o[C]=r[C])}else o=r;return c&&E(o,typeof e=="function"?e.displayName||e.name||"Unknown":e),k(e,c,o,t(),S,g)}function p(e){v(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===y&&(e._payload.status==="fulfilled"?v(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function v(e){return typeof e=="object"&&e!==null&&e.$$typeof===A}var b=d,A=Symbol.for("react.transitional.element"),V=Symbol.for("react.portal"),j=Symbol.for("react.fragment"),J=Symbol.for("react.strict_mode"),z=Symbol.for("react.profiler"),G=Symbol.for("react.consumer"),H=Symbol.for("react.context"),X=Symbol.for("react.forward_ref"),Z=Symbol.for("react.suspense"),Q=Symbol.for("react.suspense_list"),K=Symbol.for("react.memo"),y=Symbol.for("react.lazy"),ee=Symbol.for("react.activity"),re=Symbol.for("react.client.reference"),O=b.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,N=Object.prototype.hasOwnProperty,te=Array.isArray,P=console.createTask?console.createTask:function(){return null};b={react_stack_bottom_frame:function(e){return e()}};var Y,F={},$=b.react_stack_bottom_frame.bind(b,u)(),B=P(i(u)),I={};x.Fragment=j,x.jsx=function(e,r,o){var s=1e4>O.recentlyCreatedOwnerStacks++;return w(e,r,o,!1,s?Error("react-stack-top-frame"):$,s?P(i(e)):B)},x.jsxs=function(e,r,o){var s=1e4>O.recentlyCreatedOwnerStacks++;return w(e,r,o,!0,s?Error("react-stack-top-frame"):$,s?P(i(e)):B)}})()),x}var D;function se(){return D||(D=1,process.env.NODE_ENV==="production"?h.exports=ae():h.exports=ue()),h.exports}var m=se();const q=typeof window<"u"?d.useLayoutEffect:d.useEffect;function ce({bindApi:a,selector:n,children:l}){const i=W.useStore(a.store,n);return typeof l=="function"?m.jsx(m.Fragment,{children:l(i)}):m.jsx(m.Fragment,{children:l})}function le({bindApi:a,value:n,children:l}){const i=W.useStore(a.store,u=>u),t={state:{value:n},meta:{isActive:i.value===n},handleChange:()=>a.setValue(n)};return m.jsx(m.Fragment,{children:l(t)})}function U(a){const[n]=d.useState(()=>new oe.BindApi(a)),l=d.useMemo(()=>{const i=n;return i.Element=function(u){return m.jsx(le,{bindApi:n,value:u.value,children:u.children})},i.Subscribe=function(u){const f=u.selector??(E=>E);return m.jsx(ce,{bindApi:n,selector:f,children:u.children})},i},[n]);return q(n.mount,[]),q(()=>{n.update(a)}),l}d.createContext(null);d.createContext(null);function ie(){const a=d.createContext(null),n=d.createContext(null);function l(){const t=d.useContext(a);if(!t)throw new Error("`useElementContext` must be used within an `Element` component created by `createBindHook`");return t}function i(){const t=d.useContext(n);if(!t)throw new Error("`useBindContext` must be used within an `AppBind` component created by `createBindHook`");return t}return{elementContext:a,bindContext:n,useElementContext:l,useBindContext:i}}function fe({elementContext:a,bindContext:n,components:l}){function i(t,u){const f=U(u),E=l[t],T=d.useMemo(()=>({children:p})=>m.jsx(n.Provider,{value:f,children:p}),[f]),k=d.useMemo(()=>function(v){return m.jsx(f.Element,{value:v.value,children:b=>{const A={...b,...E};return m.jsx(a.Provider,{value:b,children:v.children(A)})}})},[f,E]);return d.useMemo(()=>Object.assign({},f,{Element:k,AppBind:T}),[f,k,T])}return{useAppBind:i}}exports.createBindContexts=ie;exports.createBindHook=fe;exports.useBind=U;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { BindOptions } from "@bind/bind-core";
|
|
2
|
+
import type { ComponentType, Context, FunctionComponent, PropsWithChildren, ReactNode } from "react";
|
|
3
|
+
import type { ReactBindExtendedApi } from "./useBind";
|
|
4
|
+
import type { ElementContext } from "./types";
|
|
5
|
+
/**
|
|
6
|
+
* Creates the contexts and hooks needed for bind composition.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* export const { bindContext, elementContext, useElementContext, useBindContext } =
|
|
11
|
+
* createBindContexts();
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare function createBindContexts(): {
|
|
15
|
+
elementContext: Context<ElementContext<readonly string[]>>;
|
|
16
|
+
bindContext: Context<ReactBindExtendedApi<readonly string[]>>;
|
|
17
|
+
useElementContext: <TValues extends readonly string[] = readonly string[]>() => ElementContext<TValues>;
|
|
18
|
+
useBindContext: <TValues extends readonly string[] = readonly string[]>() => ReactBindExtendedApi<TValues>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* A record of component groups, where each key is a group name
|
|
22
|
+
* and the value is a record of components in that group.
|
|
23
|
+
*/
|
|
24
|
+
type ComponentGroups = Record<string, Record<string, ComponentType<any>>>;
|
|
25
|
+
/**
|
|
26
|
+
* Props for the createBindHook factory function.
|
|
27
|
+
*/
|
|
28
|
+
interface CreateBindHookProps<TComponentGroups extends ComponentGroups> {
|
|
29
|
+
elementContext: Context<ElementContext<readonly string[]>>;
|
|
30
|
+
bindContext: Context<ReactBindExtendedApi<readonly string[]>>;
|
|
31
|
+
components: TComponentGroups;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Context passed to Element children, including the element render context
|
|
35
|
+
* and the selected component group.
|
|
36
|
+
*/
|
|
37
|
+
export type AppElementContext<TValues extends readonly string[], TComponents extends Record<string, ComponentType<any>>> = ElementContext<TValues> & TComponents;
|
|
38
|
+
/**
|
|
39
|
+
* Props for the Element component in composition API.
|
|
40
|
+
*/
|
|
41
|
+
export interface AppElementProps<TValues extends readonly string[], TComponents extends Record<string, ComponentType<any>>> {
|
|
42
|
+
/** The value this element represents */
|
|
43
|
+
value: TValues[number];
|
|
44
|
+
/** Render prop receiving the element context with components */
|
|
45
|
+
children: (context: AppElementContext<TValues, TComponents>) => ReactNode;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Extended bind API with composition support.
|
|
49
|
+
*/
|
|
50
|
+
export type AppBindExtendedApi<TValues extends readonly string[], TComponents extends Record<string, ComponentType<any>>> = Omit<ReactBindExtendedApi<TValues>, "Element"> & {
|
|
51
|
+
/**
|
|
52
|
+
* An element component that provides both element context and the selected component group.
|
|
53
|
+
* Use this to render pre-bound custom components.
|
|
54
|
+
*/
|
|
55
|
+
Element: (props: AppElementProps<TValues, TComponents>) => ReturnType<FunctionComponent>;
|
|
56
|
+
/**
|
|
57
|
+
* A wrapper component that provides bind context to children.
|
|
58
|
+
* Use this when you need access to the bind API in deeply nested components.
|
|
59
|
+
*/
|
|
60
|
+
AppBind: ComponentType<PropsWithChildren<{}>>;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Creates a custom bind hook factory with pre-bound component groups.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```tsx
|
|
67
|
+
* const { elementContext, bindContext, useElementContext } = createBindContexts();
|
|
68
|
+
*
|
|
69
|
+
* function Tab({ children }: { children: React.ReactNode }) {
|
|
70
|
+
* const element = useElementContext();
|
|
71
|
+
* return <button onClick={element.handleChange}>{children}</button>;
|
|
72
|
+
* }
|
|
73
|
+
*
|
|
74
|
+
* function TabPanel({ children }: { children: React.ReactNode }) {
|
|
75
|
+
* const element = useElementContext();
|
|
76
|
+
* if (!element.meta.isActive) return null;
|
|
77
|
+
* return <div>{children}</div>;
|
|
78
|
+
* }
|
|
79
|
+
*
|
|
80
|
+
* const { useAppBind } = createBindHook({
|
|
81
|
+
* elementContext,
|
|
82
|
+
* bindContext,
|
|
83
|
+
* components: {
|
|
84
|
+
* Tab: { Tab, TabPanel },
|
|
85
|
+
* Wizard: { Step, Button },
|
|
86
|
+
* },
|
|
87
|
+
* });
|
|
88
|
+
*
|
|
89
|
+
* function MyTabs() {
|
|
90
|
+
* const tabs = useAppBind('Tab', {
|
|
91
|
+
* defaultValue: 'tab1',
|
|
92
|
+
* values: ['tab1', 'tab2'] as const,
|
|
93
|
+
* });
|
|
94
|
+
*
|
|
95
|
+
* return (
|
|
96
|
+
* <div>
|
|
97
|
+
* <tabs.Element value="tab1">
|
|
98
|
+
* {(bindApi) => <bindApi.Tab>Tab 1</bindApi.Tab>}
|
|
99
|
+
* </tabs.Element>
|
|
100
|
+
* <tabs.Element value="tab1">
|
|
101
|
+
* {(bindApi) => <bindApi.TabPanel>Panel 1</bindApi.TabPanel>}
|
|
102
|
+
* </tabs.Element>
|
|
103
|
+
* </div>
|
|
104
|
+
* );
|
|
105
|
+
* }
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export declare function createBindHook<const TComponentGroups extends ComponentGroups>({ elementContext, bindContext, components, }: CreateBindHookProps<TComponentGroups>): {
|
|
109
|
+
useAppBind: <TGroupKey extends keyof TComponentGroups, TValues extends readonly string[]>(groupKey: TGroupKey, opts: BindOptions<TValues>) => AppBindExtendedApi<TValues, TComponentGroups[TGroupKey]>;
|
|
110
|
+
};
|
|
111
|
+
export {};
|
|
112
|
+
//# sourceMappingURL=createBindHook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createBindHook.d.ts","sourceRoot":"","sources":["../../src/createBindHook.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAa,MAAM,iBAAiB,CAAC;AAC9D,OAAO,KAAK,EACX,aAAa,EACb,OAAO,EACP,iBAAiB,EACjB,iBAAiB,EACjB,SAAS,EACT,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAc9C;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB;;;wBAKN,OAAO,SAAS,SAAS,MAAM,EAAE,2BASzC,cAAc,CAAC,OAAO,CAAC;qBAGlB,OAAO,SAAS,SAAS,MAAM,EAAE,2BAS3B,oBAAoB,CAAC,OAAO,CAAC;EAS3D;AAED;;;GAGG;AACH,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAE1E;;GAEG;AACH,UAAU,mBAAmB,CAAC,gBAAgB,SAAS,eAAe;IACrE,cAAc,EAAE,OAAO,CAAC,cAAc,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;IAC3D,WAAW,EAAE,OAAO,CAAC,oBAAoB,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;IAC9D,UAAU,EAAE,gBAAgB,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,MAAM,iBAAiB,CAC5B,OAAO,SAAS,SAAS,MAAM,EAAE,EACjC,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,IACnD,cAAc,CAAC,OAAO,CAAC,GAAG,WAAW,CAAC;AAE1C;;GAEG;AACH,MAAM,WAAW,eAAe,CAC/B,OAAO,SAAS,SAAS,MAAM,EAAE,EACjC,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;IAEtD,wCAAwC;IACxC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACvB,gEAAgE;IAChE,QAAQ,EAAE,CAAC,OAAO,EAAE,iBAAiB,CAAC,OAAO,EAAE,WAAW,CAAC,KAAK,SAAS,CAAC;CAC1E;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,CAC7B,OAAO,SAAS,SAAS,MAAM,EAAE,EACjC,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,IACnD,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,GAAG;IACpD;;;OAGG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,eAAe,CAAC,OAAO,EAAE,WAAW,CAAC,KAAK,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAEzF;;;OAGG;IACH,OAAO,EAAE,aAAa,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC;CAC9C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,cAAc,CAAC,KAAK,CAAC,gBAAgB,SAAS,eAAe,EAAE,EAC9E,cAAc,EACd,WAAW,EACX,UAAU,GACV,EAAE,mBAAmB,CAAC,gBAAgB,CAAC;iBAEtC,SAAS,SAAS,MAAM,gBAAgB,EACxC,OAAO,SAAS,SAAS,MAAM,EAAE,YAEvB,SAAS,QACb,WAAW,CAAC,OAAO,CAAC,KACxB,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC;EAqD3D"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { useBind } from "./useBind";
|
|
2
|
+
export type { ReactBindApi, ReactBindExtendedApi } from "./useBind";
|
|
3
|
+
export type { ElementContext, ElementState, ElementMeta, ElementProps, SubscribeProps, } from "./types";
|
|
4
|
+
export { createBindContexts, createBindHook } from "./createBindHook";
|
|
5
|
+
export type { AppElementContext, AppElementProps, AppBindExtendedApi } from "./createBindHook";
|
|
6
|
+
export type { BindOptions, BindState } from "@bind/core";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACpE,YAAY,EACX,cAAc,EACd,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,cAAc,GACd,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG/F,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import ae, { useLayoutEffect as ue, useEffect as se, useState as ce, useMemo as w, createContext as S, useContext as D } from "react";
|
|
2
|
+
import { BindApi as le } from "@bind/core";
|
|
3
|
+
import { useStore as q } from "@tanstack/react-store";
|
|
4
|
+
var h = { exports: {} }, v = {};
|
|
5
|
+
/**
|
|
6
|
+
* @license React
|
|
7
|
+
* react-jsx-runtime.production.js
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
10
|
+
*
|
|
11
|
+
* This source code is licensed under the MIT license found in the
|
|
12
|
+
* LICENSE file in the root directory of this source tree.
|
|
13
|
+
*/
|
|
14
|
+
var M;
|
|
15
|
+
function ie() {
|
|
16
|
+
if (M) return v;
|
|
17
|
+
M = 1;
|
|
18
|
+
var a = Symbol.for("react.transitional.element"), n = Symbol.for("react.fragment");
|
|
19
|
+
function l(i, t, u) {
|
|
20
|
+
var f = null;
|
|
21
|
+
if (u !== void 0 && (f = "" + u), t.key !== void 0 && (f = "" + t.key), "key" in t) {
|
|
22
|
+
u = {};
|
|
23
|
+
for (var m in t)
|
|
24
|
+
m !== "key" && (u[m] = t[m]);
|
|
25
|
+
} else u = t;
|
|
26
|
+
return t = u.ref, {
|
|
27
|
+
$$typeof: a,
|
|
28
|
+
type: i,
|
|
29
|
+
key: f,
|
|
30
|
+
ref: t !== void 0 ? t : null,
|
|
31
|
+
props: u
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return v.Fragment = n, v.jsx = l, v.jsxs = l, v;
|
|
35
|
+
}
|
|
36
|
+
var R = {};
|
|
37
|
+
/**
|
|
38
|
+
* @license React
|
|
39
|
+
* react-jsx-runtime.development.js
|
|
40
|
+
*
|
|
41
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
42
|
+
*
|
|
43
|
+
* This source code is licensed under the MIT license found in the
|
|
44
|
+
* LICENSE file in the root directory of this source tree.
|
|
45
|
+
*/
|
|
46
|
+
var W;
|
|
47
|
+
function fe() {
|
|
48
|
+
return W || (W = 1, process.env.NODE_ENV !== "production" && (function() {
|
|
49
|
+
function a(e) {
|
|
50
|
+
if (e == null) return null;
|
|
51
|
+
if (typeof e == "function")
|
|
52
|
+
return e.$$typeof === te ? null : e.displayName || e.name || null;
|
|
53
|
+
if (typeof e == "string") return e;
|
|
54
|
+
switch (e) {
|
|
55
|
+
case O:
|
|
56
|
+
return "Fragment";
|
|
57
|
+
case G:
|
|
58
|
+
return "Profiler";
|
|
59
|
+
case z:
|
|
60
|
+
return "StrictMode";
|
|
61
|
+
case Q:
|
|
62
|
+
return "Suspense";
|
|
63
|
+
case K:
|
|
64
|
+
return "SuspenseList";
|
|
65
|
+
case re:
|
|
66
|
+
return "Activity";
|
|
67
|
+
}
|
|
68
|
+
if (typeof e == "object")
|
|
69
|
+
switch (typeof e.tag == "number" && console.error(
|
|
70
|
+
"Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
|
|
71
|
+
), e.$$typeof) {
|
|
72
|
+
case J:
|
|
73
|
+
return "Portal";
|
|
74
|
+
case X:
|
|
75
|
+
return e.displayName || "Context";
|
|
76
|
+
case H:
|
|
77
|
+
return (e._context.displayName || "Context") + ".Consumer";
|
|
78
|
+
case Z:
|
|
79
|
+
var r = e.render;
|
|
80
|
+
return e = e.displayName, e || (e = r.displayName || r.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e;
|
|
81
|
+
case ee:
|
|
82
|
+
return r = e.displayName || null, r !== null ? r : a(e.type) || "Memo";
|
|
83
|
+
case y:
|
|
84
|
+
r = e._payload, e = e._init;
|
|
85
|
+
try {
|
|
86
|
+
return a(e(r));
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
function n(e) {
|
|
93
|
+
return "" + e;
|
|
94
|
+
}
|
|
95
|
+
function l(e) {
|
|
96
|
+
try {
|
|
97
|
+
n(e);
|
|
98
|
+
var r = !1;
|
|
99
|
+
} catch {
|
|
100
|
+
r = !0;
|
|
101
|
+
}
|
|
102
|
+
if (r) {
|
|
103
|
+
r = console;
|
|
104
|
+
var o = r.error, s = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
|
|
105
|
+
return o.call(
|
|
106
|
+
r,
|
|
107
|
+
"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
|
|
108
|
+
s
|
|
109
|
+
), n(e);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function i(e) {
|
|
113
|
+
if (e === O) return "<>";
|
|
114
|
+
if (typeof e == "object" && e !== null && e.$$typeof === y)
|
|
115
|
+
return "<...>";
|
|
116
|
+
try {
|
|
117
|
+
var r = a(e);
|
|
118
|
+
return r ? "<" + r + ">" : "<...>";
|
|
119
|
+
} catch {
|
|
120
|
+
return "<...>";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function t() {
|
|
124
|
+
var e = P.A;
|
|
125
|
+
return e === null ? null : e.getOwner();
|
|
126
|
+
}
|
|
127
|
+
function u() {
|
|
128
|
+
return Error("react-stack-top-frame");
|
|
129
|
+
}
|
|
130
|
+
function f(e) {
|
|
131
|
+
if (Y.call(e, "key")) {
|
|
132
|
+
var r = Object.getOwnPropertyDescriptor(e, "key").get;
|
|
133
|
+
if (r && r.isReactWarning) return !1;
|
|
134
|
+
}
|
|
135
|
+
return e.key !== void 0;
|
|
136
|
+
}
|
|
137
|
+
function m(e, r) {
|
|
138
|
+
function o() {
|
|
139
|
+
F || (F = !0, console.error(
|
|
140
|
+
"%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
|
|
141
|
+
r
|
|
142
|
+
));
|
|
143
|
+
}
|
|
144
|
+
o.isReactWarning = !0, Object.defineProperty(e, "key", {
|
|
145
|
+
get: o,
|
|
146
|
+
configurable: !0
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function T() {
|
|
150
|
+
var e = a(this.type);
|
|
151
|
+
return $[e] || ($[e] = !0, console.error(
|
|
152
|
+
"Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
|
|
153
|
+
)), e = this.props.ref, e !== void 0 ? e : null;
|
|
154
|
+
}
|
|
155
|
+
function x(e, r, o, s, A, C) {
|
|
156
|
+
var c = o.ref;
|
|
157
|
+
return e = {
|
|
158
|
+
$$typeof: k,
|
|
159
|
+
type: e,
|
|
160
|
+
key: r,
|
|
161
|
+
props: o,
|
|
162
|
+
_owner: s
|
|
163
|
+
}, (c !== void 0 ? c : null) !== null ? Object.defineProperty(e, "ref", {
|
|
164
|
+
enumerable: !1,
|
|
165
|
+
get: T
|
|
166
|
+
}) : Object.defineProperty(e, "ref", { enumerable: !1, value: null }), e._store = {}, Object.defineProperty(e._store, "validated", {
|
|
167
|
+
configurable: !1,
|
|
168
|
+
enumerable: !1,
|
|
169
|
+
writable: !0,
|
|
170
|
+
value: 0
|
|
171
|
+
}), Object.defineProperty(e, "_debugInfo", {
|
|
172
|
+
configurable: !1,
|
|
173
|
+
enumerable: !1,
|
|
174
|
+
writable: !0,
|
|
175
|
+
value: null
|
|
176
|
+
}), Object.defineProperty(e, "_debugStack", {
|
|
177
|
+
configurable: !1,
|
|
178
|
+
enumerable: !1,
|
|
179
|
+
writable: !0,
|
|
180
|
+
value: A
|
|
181
|
+
}), Object.defineProperty(e, "_debugTask", {
|
|
182
|
+
configurable: !1,
|
|
183
|
+
enumerable: !1,
|
|
184
|
+
writable: !0,
|
|
185
|
+
value: C
|
|
186
|
+
}), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
|
|
187
|
+
}
|
|
188
|
+
function j(e, r, o, s, A, C) {
|
|
189
|
+
var c = r.children;
|
|
190
|
+
if (c !== void 0)
|
|
191
|
+
if (s)
|
|
192
|
+
if (ne(c)) {
|
|
193
|
+
for (s = 0; s < c.length; s++)
|
|
194
|
+
b(c[s]);
|
|
195
|
+
Object.freeze && Object.freeze(c);
|
|
196
|
+
} else
|
|
197
|
+
console.error(
|
|
198
|
+
"React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
|
|
199
|
+
);
|
|
200
|
+
else b(c);
|
|
201
|
+
if (Y.call(r, "key")) {
|
|
202
|
+
c = a(e);
|
|
203
|
+
var p = Object.keys(r).filter(function(oe) {
|
|
204
|
+
return oe !== "key";
|
|
205
|
+
});
|
|
206
|
+
s = 0 < p.length ? "{key: someKey, " + p.join(": ..., ") + ": ...}" : "{key: someKey}", B[c + s] || (p = 0 < p.length ? "{" + p.join(": ..., ") + ": ...}" : "{}", console.error(
|
|
207
|
+
`A props object containing a "key" prop is being spread into JSX:
|
|
208
|
+
let props = %s;
|
|
209
|
+
<%s {...props} />
|
|
210
|
+
React keys must be passed directly to JSX without using spread:
|
|
211
|
+
let props = %s;
|
|
212
|
+
<%s key={someKey} {...props} />`,
|
|
213
|
+
s,
|
|
214
|
+
c,
|
|
215
|
+
p,
|
|
216
|
+
c
|
|
217
|
+
), B[c + s] = !0);
|
|
218
|
+
}
|
|
219
|
+
if (c = null, o !== void 0 && (l(o), c = "" + o), f(r) && (l(r.key), c = "" + r.key), "key" in r) {
|
|
220
|
+
o = {};
|
|
221
|
+
for (var N in r)
|
|
222
|
+
N !== "key" && (o[N] = r[N]);
|
|
223
|
+
} else o = r;
|
|
224
|
+
return c && m(
|
|
225
|
+
o,
|
|
226
|
+
typeof e == "function" ? e.displayName || e.name || "Unknown" : e
|
|
227
|
+
), x(
|
|
228
|
+
e,
|
|
229
|
+
c,
|
|
230
|
+
o,
|
|
231
|
+
t(),
|
|
232
|
+
A,
|
|
233
|
+
C
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
function b(e) {
|
|
237
|
+
_(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e !== null && e.$$typeof === y && (e._payload.status === "fulfilled" ? _(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
|
|
238
|
+
}
|
|
239
|
+
function _(e) {
|
|
240
|
+
return typeof e == "object" && e !== null && e.$$typeof === k;
|
|
241
|
+
}
|
|
242
|
+
var E = ae, k = Symbol.for("react.transitional.element"), J = Symbol.for("react.portal"), O = Symbol.for("react.fragment"), z = Symbol.for("react.strict_mode"), G = Symbol.for("react.profiler"), H = Symbol.for("react.consumer"), X = Symbol.for("react.context"), Z = Symbol.for("react.forward_ref"), Q = Symbol.for("react.suspense"), K = Symbol.for("react.suspense_list"), ee = Symbol.for("react.memo"), y = Symbol.for("react.lazy"), re = Symbol.for("react.activity"), te = Symbol.for("react.client.reference"), P = E.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, Y = Object.prototype.hasOwnProperty, ne = Array.isArray, g = console.createTask ? console.createTask : function() {
|
|
243
|
+
return null;
|
|
244
|
+
};
|
|
245
|
+
E = {
|
|
246
|
+
react_stack_bottom_frame: function(e) {
|
|
247
|
+
return e();
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
var F, $ = {}, I = E.react_stack_bottom_frame.bind(
|
|
251
|
+
E,
|
|
252
|
+
u
|
|
253
|
+
)(), L = g(i(u)), B = {};
|
|
254
|
+
R.Fragment = O, R.jsx = function(e, r, o) {
|
|
255
|
+
var s = 1e4 > P.recentlyCreatedOwnerStacks++;
|
|
256
|
+
return j(
|
|
257
|
+
e,
|
|
258
|
+
r,
|
|
259
|
+
o,
|
|
260
|
+
!1,
|
|
261
|
+
s ? Error("react-stack-top-frame") : I,
|
|
262
|
+
s ? g(i(e)) : L
|
|
263
|
+
);
|
|
264
|
+
}, R.jsxs = function(e, r, o) {
|
|
265
|
+
var s = 1e4 > P.recentlyCreatedOwnerStacks++;
|
|
266
|
+
return j(
|
|
267
|
+
e,
|
|
268
|
+
r,
|
|
269
|
+
o,
|
|
270
|
+
!0,
|
|
271
|
+
s ? Error("react-stack-top-frame") : I,
|
|
272
|
+
s ? g(i(e)) : L
|
|
273
|
+
);
|
|
274
|
+
};
|
|
275
|
+
})()), R;
|
|
276
|
+
}
|
|
277
|
+
var U;
|
|
278
|
+
function de() {
|
|
279
|
+
return U || (U = 1, process.env.NODE_ENV === "production" ? h.exports = ie() : h.exports = fe()), h.exports;
|
|
280
|
+
}
|
|
281
|
+
var d = de();
|
|
282
|
+
const V = typeof window < "u" ? ue : se;
|
|
283
|
+
function me({
|
|
284
|
+
bindApi: a,
|
|
285
|
+
selector: n,
|
|
286
|
+
children: l
|
|
287
|
+
}) {
|
|
288
|
+
const i = q(a.store, n);
|
|
289
|
+
return typeof l == "function" ? /* @__PURE__ */ d.jsx(d.Fragment, { children: l(i) }) : /* @__PURE__ */ d.jsx(d.Fragment, { children: l });
|
|
290
|
+
}
|
|
291
|
+
function Ee({
|
|
292
|
+
bindApi: a,
|
|
293
|
+
value: n,
|
|
294
|
+
children: l
|
|
295
|
+
}) {
|
|
296
|
+
const i = q(a.store, (u) => u), t = {
|
|
297
|
+
state: {
|
|
298
|
+
value: n
|
|
299
|
+
},
|
|
300
|
+
meta: {
|
|
301
|
+
isActive: i.value === n
|
|
302
|
+
},
|
|
303
|
+
handleChange: () => a.setValue(n)
|
|
304
|
+
};
|
|
305
|
+
return /* @__PURE__ */ d.jsx(d.Fragment, { children: l(t) });
|
|
306
|
+
}
|
|
307
|
+
function pe(a) {
|
|
308
|
+
const [n] = ce(() => new le(a)), l = w(() => {
|
|
309
|
+
const i = n;
|
|
310
|
+
return i.Element = function(u) {
|
|
311
|
+
return /* @__PURE__ */ d.jsx(Ee, { bindApi: n, value: u.value, children: u.children });
|
|
312
|
+
}, i.Subscribe = function(u) {
|
|
313
|
+
const f = u.selector ?? ((m) => m);
|
|
314
|
+
return /* @__PURE__ */ d.jsx(me, { bindApi: n, selector: f, children: u.children });
|
|
315
|
+
}, i;
|
|
316
|
+
}, [n]);
|
|
317
|
+
return V(n.mount, []), V(() => {
|
|
318
|
+
n.update(a);
|
|
319
|
+
}), l;
|
|
320
|
+
}
|
|
321
|
+
S(null);
|
|
322
|
+
S(null);
|
|
323
|
+
function Re() {
|
|
324
|
+
const a = S(null), n = S(null);
|
|
325
|
+
function l() {
|
|
326
|
+
const t = D(a);
|
|
327
|
+
if (!t)
|
|
328
|
+
throw new Error(
|
|
329
|
+
"`useElementContext` must be used within an `Element` component created by `createBindHook`"
|
|
330
|
+
);
|
|
331
|
+
return t;
|
|
332
|
+
}
|
|
333
|
+
function i() {
|
|
334
|
+
const t = D(n);
|
|
335
|
+
if (!t)
|
|
336
|
+
throw new Error(
|
|
337
|
+
"`useBindContext` must be used within an `AppBind` component created by `createBindHook`"
|
|
338
|
+
);
|
|
339
|
+
return t;
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
elementContext: a,
|
|
343
|
+
bindContext: n,
|
|
344
|
+
useElementContext: l,
|
|
345
|
+
useBindContext: i
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function Te({
|
|
349
|
+
elementContext: a,
|
|
350
|
+
bindContext: n,
|
|
351
|
+
components: l
|
|
352
|
+
}) {
|
|
353
|
+
function i(t, u) {
|
|
354
|
+
const f = pe(u), m = l[t], T = w(() => ({ children: b }) => /* @__PURE__ */ d.jsx(
|
|
355
|
+
n.Provider,
|
|
356
|
+
{
|
|
357
|
+
value: f,
|
|
358
|
+
children: b
|
|
359
|
+
}
|
|
360
|
+
), [f]), x = w(() => function(_) {
|
|
361
|
+
return /* @__PURE__ */ d.jsx(f.Element, { value: _.value, children: (E) => {
|
|
362
|
+
const k = {
|
|
363
|
+
...E,
|
|
364
|
+
...m
|
|
365
|
+
};
|
|
366
|
+
return /* @__PURE__ */ d.jsx(
|
|
367
|
+
a.Provider,
|
|
368
|
+
{
|
|
369
|
+
value: E,
|
|
370
|
+
children: _.children(k)
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
} });
|
|
374
|
+
}, [f, m]);
|
|
375
|
+
return w(() => Object.assign({}, f, {
|
|
376
|
+
Element: x,
|
|
377
|
+
AppBind: T
|
|
378
|
+
}), [f, x, T]);
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
useAppBind: i
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
export {
|
|
385
|
+
Re as createBindContexts,
|
|
386
|
+
Te as createBindHook,
|
|
387
|
+
pe as useBind
|
|
388
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { BindState } from "@bind/core";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* State object for Element context
|
|
5
|
+
*/
|
|
6
|
+
export interface ElementState<TValues extends readonly string[]> {
|
|
7
|
+
/** The value this element represents */
|
|
8
|
+
value: TValues[number];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Meta information for Element context
|
|
12
|
+
*/
|
|
13
|
+
export interface ElementMeta {
|
|
14
|
+
/** Whether this value is currently active */
|
|
15
|
+
isActive: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Context provided to Element render props
|
|
19
|
+
*/
|
|
20
|
+
export interface ElementContext<TValues extends readonly string[]> {
|
|
21
|
+
/** State object containing the element's value */
|
|
22
|
+
state: ElementState<TValues>;
|
|
23
|
+
/** Meta information about the element */
|
|
24
|
+
meta: ElementMeta;
|
|
25
|
+
/** Handler to set this value as active */
|
|
26
|
+
handleChange: () => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Props for the Element component
|
|
30
|
+
*/
|
|
31
|
+
export interface ElementProps<TValues extends readonly string[]> {
|
|
32
|
+
/** The value this element represents */
|
|
33
|
+
value: TValues[number];
|
|
34
|
+
/** Render prop receiving the element context */
|
|
35
|
+
children: (context: ElementContext<TValues>) => ReactNode;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Props for the Subscribe component
|
|
39
|
+
*/
|
|
40
|
+
export interface SubscribeProps<TValues extends readonly string[], TSelected = BindState<TValues>> {
|
|
41
|
+
/** Optional selector to pick specific state */
|
|
42
|
+
selector?: (state: BindState<TValues>) => TSelected;
|
|
43
|
+
/** Render prop receiving the selected state */
|
|
44
|
+
children: ((state: TSelected) => ReactNode) | ReactNode;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,OAAO,SAAS,SAAS,MAAM,EAAE;IAC9D,wCAAwC;IACxC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,6CAA6C;IAC7C,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,OAAO,SAAS,SAAS,MAAM,EAAE;IAChE,kDAAkD;IAClD,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;IAC7B,yCAAyC;IACzC,IAAI,EAAE,WAAW,CAAC;IAClB,0CAA0C;IAC1C,YAAY,EAAE,MAAM,IAAI,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,OAAO,SAAS,SAAS,MAAM,EAAE;IAC9D,wCAAwC;IACxC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACvB,gDAAgD;IAChD,QAAQ,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC;CAC1D;AAED;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,OAAO,SAAS,SAAS,MAAM,EAAE,EAAE,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC;IAChG,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC;IACpD,+CAA+C;IAC/C,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,SAAS,KAAK,SAAS,CAAC,GAAG,SAAS,CAAC;CACxD"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { BindApi, type BindOptions, type BindState } from "@bind/core";
|
|
2
|
+
import type { ElementProps, SubscribeProps } from "./types";
|
|
3
|
+
import type { FunctionComponent } from "react";
|
|
4
|
+
/**
|
|
5
|
+
* React-specific API additions to BindApi
|
|
6
|
+
*/
|
|
7
|
+
export interface ReactBindApi<TValues extends readonly string[]> {
|
|
8
|
+
/**
|
|
9
|
+
* A component that renders based on a specific value.
|
|
10
|
+
* Provides context for both reading state and setting the value.
|
|
11
|
+
*/
|
|
12
|
+
Element: (props: ElementProps<TValues>) => ReturnType<FunctionComponent>;
|
|
13
|
+
/**
|
|
14
|
+
* A component that subscribes to bind state changes.
|
|
15
|
+
* Use the selector prop to optimize re-renders.
|
|
16
|
+
*/
|
|
17
|
+
Subscribe: <TSelected = BindState<TValues>>(props: SubscribeProps<TValues, TSelected>) => ReturnType<FunctionComponent>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The extended API returned by useBind, combining core API with React components
|
|
21
|
+
*/
|
|
22
|
+
export type ReactBindExtendedApi<TValues extends readonly string[]> = BindApi<TValues> & ReactBindApi<TValues>;
|
|
23
|
+
/**
|
|
24
|
+
* A custom React Hook that returns an extended instance of the `BindApi` class.
|
|
25
|
+
*
|
|
26
|
+
* This API encapsulates all the necessary functionalities for managing exclusive selection state
|
|
27
|
+
* like tabs, accordions, and multi-step forms.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* const tabs = useBind({
|
|
32
|
+
* defaultValue: "tab1",
|
|
33
|
+
* values: ["tab1", "tab2"] as const,
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* return (
|
|
37
|
+
* <div>
|
|
38
|
+
* <tabs.Element value="tab1">
|
|
39
|
+
* {(bindApi) => <button onClick={bind.handleChange}>Tab 1</button>}
|
|
40
|
+
* </tabs.Element>
|
|
41
|
+
* <tabs.Element value="tab1">
|
|
42
|
+
* {(bindApi) => bind.meta.isActive && <div>Panel 1</div>}
|
|
43
|
+
* </tabs.Element>
|
|
44
|
+
* </div>
|
|
45
|
+
* );
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function useBind<TValues extends readonly string[]>(opts: BindOptions<TValues>): ReactBindExtendedApi<TValues>;
|
|
49
|
+
//# sourceMappingURL=useBind.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBind.d.ts","sourceRoot":"","sources":["../../src/useBind.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAIvE,OAAO,KAAK,EAAE,YAAY,EAAkB,cAAc,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,KAAK,EAAE,iBAAiB,EAAa,MAAM,OAAO,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,OAAO,SAAS,SAAS,MAAM,EAAE;IAC9D;;;OAGG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAEzE;;;OAGG;IACH,SAAS,EAAE,CAAC,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,EACzC,KAAK,EAAE,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC,KACrC,UAAU,CAAC,iBAAiB,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,MAAM,oBAAoB,CAAC,OAAO,SAAS,SAAS,MAAM,EAAE,IAAI,OAAO,CAAC,OAAO,CAAC,GACrF,YAAY,CAAC,OAAO,CAAC,CAAC;AAiDvB;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,OAAO,CAAC,OAAO,SAAS,SAAS,MAAM,EAAE,EACxD,IAAI,EAAE,WAAW,CAAC,OAAO,CAAC,GACxB,oBAAoB,CAAC,OAAO,CAAC,CAmC/B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIsomorphicLayoutEffect.d.ts","sourceRoot":"","sources":["../../src/useIsomorphicLayoutEffect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,eAAe,EAAE,MAAM,OAAO,CAAC;AAEnD;;GAEG;AACH,eAAO,MAAM,yBAAyB,wBACsB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bind-ts/react-bind",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "React bindings for @bind-ts/core - type-safe compound component binding",
|
|
5
|
+
"author": "",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"types": "dist/esm/index.d.ts",
|
|
9
|
+
"main": "dist/cjs/index.cjs",
|
|
10
|
+
"module": "dist/esm/index.js",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": {
|
|
14
|
+
"types": "./dist/esm/index.d.ts",
|
|
15
|
+
"default": "./dist/esm/index.js"
|
|
16
|
+
},
|
|
17
|
+
"require": {
|
|
18
|
+
"types": "./dist/cjs/index.d.cts",
|
|
19
|
+
"default": "./dist/cjs/index.cjs"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"src",
|
|
28
|
+
"!**/__tests__"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "vite build && tsc --emitDeclarationOnly --outDir dist/esm",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"check-types": "tsc --noEmit"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@bind-ts/bind-core": "^0.0.1",
|
|
38
|
+
"@tanstack/react-store": "^0.7.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"react": ">=18.0.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@testing-library/jest-dom": "^6.6.0",
|
|
45
|
+
"@testing-library/react": "^16.0.0",
|
|
46
|
+
"@types/react": "^19.0.0",
|
|
47
|
+
"jsdom": "^26.1.0",
|
|
48
|
+
"react": "^19.0.0",
|
|
49
|
+
"react-dom": "^19.0.0",
|
|
50
|
+
"typescript": "^5.9.2",
|
|
51
|
+
"vite": "^6.3.5",
|
|
52
|
+
"vitest": "^3.2.4"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext, useMemo } from "react";
|
|
4
|
+
import { useBind } from "./useBind";
|
|
5
|
+
import type { BindOptions, BindState } from "@bind/bind-core";
|
|
6
|
+
import type {
|
|
7
|
+
ComponentType,
|
|
8
|
+
Context,
|
|
9
|
+
FunctionComponent,
|
|
10
|
+
PropsWithChildren,
|
|
11
|
+
ReactNode,
|
|
12
|
+
} from "react";
|
|
13
|
+
import type { ReactBindExtendedApi } from "./useBind";
|
|
14
|
+
import type { ElementContext } from "./types";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Context for the element render context, providing access to value state
|
|
18
|
+
* within custom element components.
|
|
19
|
+
*/
|
|
20
|
+
const defaultElementContext = createContext<ElementContext<readonly string[]>>(null as never);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Context for the bind API, providing access to the bind instance
|
|
24
|
+
* within custom components.
|
|
25
|
+
*/
|
|
26
|
+
const defaultBindContext = createContext<ReactBindExtendedApi<readonly string[]>>(null as never);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates the contexts and hooks needed for bind composition.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```tsx
|
|
33
|
+
* export const { bindContext, elementContext, useElementContext, useBindContext } =
|
|
34
|
+
* createBindContexts();
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function createBindContexts() {
|
|
38
|
+
const elementContext = createContext<ElementContext<readonly string[]>>(null as never);
|
|
39
|
+
|
|
40
|
+
const bindContext = createContext<ReactBindExtendedApi<readonly string[]>>(null as never);
|
|
41
|
+
|
|
42
|
+
function useElementContext<TValues extends readonly string[] = readonly string[]>() {
|
|
43
|
+
const element = useContext(elementContext);
|
|
44
|
+
|
|
45
|
+
if (!element) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"`useElementContext` must be used within an `Element` component created by `createBindHook`",
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return element as ElementContext<TValues>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function useBindContext<TValues extends readonly string[] = readonly string[]>() {
|
|
55
|
+
const bindApi = useContext(bindContext);
|
|
56
|
+
|
|
57
|
+
if (!bindApi) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
"`useBindContext` must be used within an `AppBind` component created by `createBindHook`",
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return bindApi as unknown as ReactBindExtendedApi<TValues>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
elementContext,
|
|
68
|
+
bindContext,
|
|
69
|
+
useElementContext,
|
|
70
|
+
useBindContext,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* A record of component groups, where each key is a group name
|
|
76
|
+
* and the value is a record of components in that group.
|
|
77
|
+
*/
|
|
78
|
+
type ComponentGroups = Record<string, Record<string, ComponentType<any>>>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Props for the createBindHook factory function.
|
|
82
|
+
*/
|
|
83
|
+
interface CreateBindHookProps<TComponentGroups extends ComponentGroups> {
|
|
84
|
+
elementContext: Context<ElementContext<readonly string[]>>;
|
|
85
|
+
bindContext: Context<ReactBindExtendedApi<readonly string[]>>;
|
|
86
|
+
components: TComponentGroups;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Context passed to Element children, including the element render context
|
|
91
|
+
* and the selected component group.
|
|
92
|
+
*/
|
|
93
|
+
export type AppElementContext<
|
|
94
|
+
TValues extends readonly string[],
|
|
95
|
+
TComponents extends Record<string, ComponentType<any>>,
|
|
96
|
+
> = ElementContext<TValues> & TComponents;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Props for the Element component in composition API.
|
|
100
|
+
*/
|
|
101
|
+
export interface AppElementProps<
|
|
102
|
+
TValues extends readonly string[],
|
|
103
|
+
TComponents extends Record<string, ComponentType<any>>,
|
|
104
|
+
> {
|
|
105
|
+
/** The value this element represents */
|
|
106
|
+
value: TValues[number];
|
|
107
|
+
/** Render prop receiving the element context with components */
|
|
108
|
+
children: (context: AppElementContext<TValues, TComponents>) => ReactNode;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Extended bind API with composition support.
|
|
113
|
+
*/
|
|
114
|
+
export type AppBindExtendedApi<
|
|
115
|
+
TValues extends readonly string[],
|
|
116
|
+
TComponents extends Record<string, ComponentType<any>>,
|
|
117
|
+
> = Omit<ReactBindExtendedApi<TValues>, "Element"> & {
|
|
118
|
+
/**
|
|
119
|
+
* An element component that provides both element context and the selected component group.
|
|
120
|
+
* Use this to render pre-bound custom components.
|
|
121
|
+
*/
|
|
122
|
+
Element: (props: AppElementProps<TValues, TComponents>) => ReturnType<FunctionComponent>;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* A wrapper component that provides bind context to children.
|
|
126
|
+
* Use this when you need access to the bind API in deeply nested components.
|
|
127
|
+
*/
|
|
128
|
+
AppBind: ComponentType<PropsWithChildren<{}>>;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Creates a custom bind hook factory with pre-bound component groups.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```tsx
|
|
136
|
+
* const { elementContext, bindContext, useElementContext } = createBindContexts();
|
|
137
|
+
*
|
|
138
|
+
* function Tab({ children }: { children: React.ReactNode }) {
|
|
139
|
+
* const element = useElementContext();
|
|
140
|
+
* return <button onClick={element.handleChange}>{children}</button>;
|
|
141
|
+
* }
|
|
142
|
+
*
|
|
143
|
+
* function TabPanel({ children }: { children: React.ReactNode }) {
|
|
144
|
+
* const element = useElementContext();
|
|
145
|
+
* if (!element.meta.isActive) return null;
|
|
146
|
+
* return <div>{children}</div>;
|
|
147
|
+
* }
|
|
148
|
+
*
|
|
149
|
+
* const { useAppBind } = createBindHook({
|
|
150
|
+
* elementContext,
|
|
151
|
+
* bindContext,
|
|
152
|
+
* components: {
|
|
153
|
+
* Tab: { Tab, TabPanel },
|
|
154
|
+
* Wizard: { Step, Button },
|
|
155
|
+
* },
|
|
156
|
+
* });
|
|
157
|
+
*
|
|
158
|
+
* function MyTabs() {
|
|
159
|
+
* const tabs = useAppBind('Tab', {
|
|
160
|
+
* defaultValue: 'tab1',
|
|
161
|
+
* values: ['tab1', 'tab2'] as const,
|
|
162
|
+
* });
|
|
163
|
+
*
|
|
164
|
+
* return (
|
|
165
|
+
* <div>
|
|
166
|
+
* <tabs.Element value="tab1">
|
|
167
|
+
* {(bindApi) => <bindApi.Tab>Tab 1</bindApi.Tab>}
|
|
168
|
+
* </tabs.Element>
|
|
169
|
+
* <tabs.Element value="tab1">
|
|
170
|
+
* {(bindApi) => <bindApi.TabPanel>Panel 1</bindApi.TabPanel>}
|
|
171
|
+
* </tabs.Element>
|
|
172
|
+
* </div>
|
|
173
|
+
* );
|
|
174
|
+
* }
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
export function createBindHook<const TComponentGroups extends ComponentGroups>({
|
|
178
|
+
elementContext,
|
|
179
|
+
bindContext,
|
|
180
|
+
components,
|
|
181
|
+
}: CreateBindHookProps<TComponentGroups>) {
|
|
182
|
+
function useAppBind<
|
|
183
|
+
TGroupKey extends keyof TComponentGroups,
|
|
184
|
+
TValues extends readonly string[],
|
|
185
|
+
>(
|
|
186
|
+
groupKey: TGroupKey,
|
|
187
|
+
opts: BindOptions<TValues>,
|
|
188
|
+
): AppBindExtendedApi<TValues, TComponentGroups[TGroupKey]> {
|
|
189
|
+
const bindApi = useBind(opts);
|
|
190
|
+
const componentGroup = components[groupKey];
|
|
191
|
+
|
|
192
|
+
const AppBind = useMemo<ComponentType<PropsWithChildren<{}>>>(() => {
|
|
193
|
+
return ({ children }) => {
|
|
194
|
+
return (
|
|
195
|
+
<bindContext.Provider
|
|
196
|
+
value={bindApi as unknown as ReactBindExtendedApi<readonly string[]>}
|
|
197
|
+
>
|
|
198
|
+
{children}
|
|
199
|
+
</bindContext.Provider>
|
|
200
|
+
);
|
|
201
|
+
};
|
|
202
|
+
}, [bindApi]);
|
|
203
|
+
|
|
204
|
+
const Element = useMemo(() => {
|
|
205
|
+
return function Element(props: AppElementProps<TValues, TComponentGroups[TGroupKey]>) {
|
|
206
|
+
return (
|
|
207
|
+
<bindApi.Element value={props.value}>
|
|
208
|
+
{(bindApi) => {
|
|
209
|
+
// Merge element context with component group
|
|
210
|
+
const appElementContext = {
|
|
211
|
+
...bindApi,
|
|
212
|
+
...componentGroup,
|
|
213
|
+
} as AppElementContext<TValues, TComponentGroups[TGroupKey]>;
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<elementContext.Provider
|
|
217
|
+
value={bindApi as ElementContext<readonly string[]>}
|
|
218
|
+
>
|
|
219
|
+
{props.children(appElementContext)}
|
|
220
|
+
</elementContext.Provider>
|
|
221
|
+
);
|
|
222
|
+
}}
|
|
223
|
+
</bindApi.Element>
|
|
224
|
+
);
|
|
225
|
+
};
|
|
226
|
+
}, [bindApi, componentGroup]);
|
|
227
|
+
|
|
228
|
+
const extendedApi = useMemo(() => {
|
|
229
|
+
return Object.assign({}, bindApi, {
|
|
230
|
+
Element,
|
|
231
|
+
AppBind,
|
|
232
|
+
}) as AppBindExtendedApi<TValues, TComponentGroups[TGroupKey]>;
|
|
233
|
+
}, [bindApi, Element, AppBind]);
|
|
234
|
+
|
|
235
|
+
return extendedApi;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
useAppBind,
|
|
240
|
+
};
|
|
241
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { useBind } from "./useBind";
|
|
2
|
+
export type { ReactBindApi, ReactBindExtendedApi } from "./useBind";
|
|
3
|
+
export type {
|
|
4
|
+
ElementContext,
|
|
5
|
+
ElementState,
|
|
6
|
+
ElementMeta,
|
|
7
|
+
ElementProps,
|
|
8
|
+
SubscribeProps,
|
|
9
|
+
} from "./types";
|
|
10
|
+
|
|
11
|
+
// Composition APIs
|
|
12
|
+
export { createBindContexts, createBindHook } from "./createBindHook";
|
|
13
|
+
export type { AppElementContext, AppElementProps, AppBindExtendedApi } from "./createBindHook";
|
|
14
|
+
|
|
15
|
+
// Re-export core types for convenience
|
|
16
|
+
export type { BindOptions, BindState } from "@bind/core";
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { BindState } from "@bind/core";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* State object for Element context
|
|
6
|
+
*/
|
|
7
|
+
export interface ElementState<TValues extends readonly string[]> {
|
|
8
|
+
/** The value this element represents */
|
|
9
|
+
value: TValues[number];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Meta information for Element context
|
|
14
|
+
*/
|
|
15
|
+
export interface ElementMeta {
|
|
16
|
+
/** Whether this value is currently active */
|
|
17
|
+
isActive: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Context provided to Element render props
|
|
22
|
+
*/
|
|
23
|
+
export interface ElementContext<TValues extends readonly string[]> {
|
|
24
|
+
/** State object containing the element's value */
|
|
25
|
+
state: ElementState<TValues>;
|
|
26
|
+
/** Meta information about the element */
|
|
27
|
+
meta: ElementMeta;
|
|
28
|
+
/** Handler to set this value as active */
|
|
29
|
+
handleChange: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Props for the Element component
|
|
34
|
+
*/
|
|
35
|
+
export interface ElementProps<TValues extends readonly string[]> {
|
|
36
|
+
/** The value this element represents */
|
|
37
|
+
value: TValues[number];
|
|
38
|
+
/** Render prop receiving the element context */
|
|
39
|
+
children: (context: ElementContext<TValues>) => ReactNode;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Props for the Subscribe component
|
|
44
|
+
*/
|
|
45
|
+
export interface SubscribeProps<TValues extends readonly string[], TSelected = BindState<TValues>> {
|
|
46
|
+
/** Optional selector to pick specific state */
|
|
47
|
+
selector?: (state: BindState<TValues>) => TSelected;
|
|
48
|
+
/** Render prop receiving the selected state */
|
|
49
|
+
children: ((state: TSelected) => ReactNode) | ReactNode;
|
|
50
|
+
}
|
package/src/useBind.tsx
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { BindApi, type BindOptions, type BindState } from "@bind/core";
|
|
4
|
+
import { useStore } from "@tanstack/react-store";
|
|
5
|
+
import { useMemo, useState } from "react";
|
|
6
|
+
import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
|
|
7
|
+
import type { ElementProps, ElementContext, SubscribeProps } from "./types";
|
|
8
|
+
import type { FunctionComponent, ReactNode } from "react";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* React-specific API additions to BindApi
|
|
12
|
+
*/
|
|
13
|
+
export interface ReactBindApi<TValues extends readonly string[]> {
|
|
14
|
+
/**
|
|
15
|
+
* A component that renders based on a specific value.
|
|
16
|
+
* Provides context for both reading state and setting the value.
|
|
17
|
+
*/
|
|
18
|
+
Element: (props: ElementProps<TValues>) => ReturnType<FunctionComponent>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A component that subscribes to bind state changes.
|
|
22
|
+
* Use the selector prop to optimize re-renders.
|
|
23
|
+
*/
|
|
24
|
+
Subscribe: <TSelected = BindState<TValues>>(
|
|
25
|
+
props: SubscribeProps<TValues, TSelected>,
|
|
26
|
+
) => ReturnType<FunctionComponent>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The extended API returned by useBind, combining core API with React components
|
|
31
|
+
*/
|
|
32
|
+
export type ReactBindExtendedApi<TValues extends readonly string[]> = BindApi<TValues> &
|
|
33
|
+
ReactBindApi<TValues>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Internal Subscribe component that handles store subscription
|
|
37
|
+
*/
|
|
38
|
+
function LocalSubscribe<TValues extends readonly string[], TSelected>({
|
|
39
|
+
bindApi,
|
|
40
|
+
selector,
|
|
41
|
+
children,
|
|
42
|
+
}: {
|
|
43
|
+
bindApi: BindApi<TValues>;
|
|
44
|
+
selector: (state: BindState<TValues>) => TSelected;
|
|
45
|
+
children: ((state: TSelected) => ReactNode) | ReactNode;
|
|
46
|
+
}): ReturnType<FunctionComponent> {
|
|
47
|
+
const data = useStore(bindApi.store, selector);
|
|
48
|
+
|
|
49
|
+
if (typeof children === "function") {
|
|
50
|
+
return <>{children(data)}</>;
|
|
51
|
+
}
|
|
52
|
+
return <>{children}</>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Internal Element component that renders based on value state
|
|
57
|
+
*/
|
|
58
|
+
function LocalElement<TValues extends readonly string[]>({
|
|
59
|
+
bindApi,
|
|
60
|
+
value,
|
|
61
|
+
children,
|
|
62
|
+
}: {
|
|
63
|
+
bindApi: BindApi<TValues>;
|
|
64
|
+
value: TValues[number];
|
|
65
|
+
children: (context: ElementContext<TValues>) => ReactNode;
|
|
66
|
+
}): ReturnType<FunctionComponent> {
|
|
67
|
+
const storeState = useStore(bindApi.store, (s: BindState<TValues>) => s);
|
|
68
|
+
|
|
69
|
+
const context: ElementContext<TValues> = {
|
|
70
|
+
state: {
|
|
71
|
+
value: value,
|
|
72
|
+
},
|
|
73
|
+
meta: {
|
|
74
|
+
isActive: storeState.value === value,
|
|
75
|
+
},
|
|
76
|
+
handleChange: () => bindApi.setValue(value),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return <>{children(context)}</>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* A custom React Hook that returns an extended instance of the `BindApi` class.
|
|
84
|
+
*
|
|
85
|
+
* This API encapsulates all the necessary functionalities for managing exclusive selection state
|
|
86
|
+
* like tabs, accordions, and multi-step forms.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```tsx
|
|
90
|
+
* const tabs = useBind({
|
|
91
|
+
* defaultValue: "tab1",
|
|
92
|
+
* values: ["tab1", "tab2"] as const,
|
|
93
|
+
* });
|
|
94
|
+
*
|
|
95
|
+
* return (
|
|
96
|
+
* <div>
|
|
97
|
+
* <tabs.Element value="tab1">
|
|
98
|
+
* {(bindApi) => <button onClick={bind.handleChange}>Tab 1</button>}
|
|
99
|
+
* </tabs.Element>
|
|
100
|
+
* <tabs.Element value="tab1">
|
|
101
|
+
* {(bindApi) => bind.meta.isActive && <div>Panel 1</div>}
|
|
102
|
+
* </tabs.Element>
|
|
103
|
+
* </div>
|
|
104
|
+
* );
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export function useBind<TValues extends readonly string[]>(
|
|
108
|
+
opts: BindOptions<TValues>,
|
|
109
|
+
): ReactBindExtendedApi<TValues> {
|
|
110
|
+
const [bindApi] = useState(() => new BindApi<TValues>(opts));
|
|
111
|
+
|
|
112
|
+
const extendedApi = useMemo(() => {
|
|
113
|
+
const api = bindApi as ReactBindExtendedApi<TValues>;
|
|
114
|
+
|
|
115
|
+
api.Element = function Element(props: ElementProps<TValues>) {
|
|
116
|
+
return (
|
|
117
|
+
<LocalElement bindApi={bindApi} value={props.value}>
|
|
118
|
+
{props.children}
|
|
119
|
+
</LocalElement>
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
api.Subscribe = function Subscribe<TSelected = BindState<TValues>>(
|
|
124
|
+
props: SubscribeProps<TValues, TSelected>,
|
|
125
|
+
) {
|
|
126
|
+
const selector = props.selector ?? ((s) => s as unknown as TSelected);
|
|
127
|
+
return (
|
|
128
|
+
<LocalSubscribe bindApi={bindApi} selector={selector} children={props.children} />
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
return api;
|
|
133
|
+
}, [bindApi]);
|
|
134
|
+
|
|
135
|
+
// Mount/unmount lifecycle
|
|
136
|
+
useIsomorphicLayoutEffect(bindApi.mount, []);
|
|
137
|
+
|
|
138
|
+
// Keep options in sync (like a ref, no side effects)
|
|
139
|
+
useIsomorphicLayoutEffect(() => {
|
|
140
|
+
bindApi.update(opts);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
return extendedApi;
|
|
144
|
+
}
|