@moontra/moonui 5.0.1 → 5.1.0

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.
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+
3
+ interface UseIntersectionObserverOptions extends IntersectionObserverInit {
4
+ freezeOnceVisible?: boolean;
5
+ initialIsIntersecting?: boolean;
6
+ onChange?: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void;
7
+ }
8
+ declare function useIntersectionObserver({ threshold, root, rootMargin, freezeOnceVisible, initialIsIntersecting, onChange, }?: UseIntersectionObserverOptions): {
9
+ ref: React.Dispatch<React.SetStateAction<Element>>;
10
+ isIntersecting: boolean;
11
+ };
12
+
13
+ declare function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T | ((val: T) => T)) => void];
14
+
15
+ export { UseIntersectionObserverOptions, useIntersectionObserver, useLocalStorage };
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+
3
+ interface UseIntersectionObserverOptions extends IntersectionObserverInit {
4
+ freezeOnceVisible?: boolean;
5
+ initialIsIntersecting?: boolean;
6
+ onChange?: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void;
7
+ }
8
+ declare function useIntersectionObserver({ threshold, root, rootMargin, freezeOnceVisible, initialIsIntersecting, onChange, }?: UseIntersectionObserverOptions): {
9
+ ref: React.Dispatch<React.SetStateAction<Element>>;
10
+ isIntersecting: boolean;
11
+ };
12
+
13
+ declare function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T | ((val: T) => T)) => void];
14
+
15
+ export { UseIntersectionObserverOptions, useIntersectionObserver, useLocalStorage };
@@ -0,0 +1,38 @@
1
+ 'use client';
2
+
3
+ /** @license MoonUI v1.0.0 - MIT License - https://moonui.dev */
4
+ "use client";import{c as u,g as p,h as b}from"../chunk-VXDFZPDA.global.js";var n=u(p()),O=u(b());function E({threshold:t=0,root:a=null,rootMargin:i="0%",freezeOnceVisible:o=!1,initialIsIntersecting:d=!1,onChange:e}={}){let[r,s]=n.useState(null),[c,m]=n.useState(d),f=n.useRef(!1);return n.useEffect(()=>{if(!r||!window.IntersectionObserver||f.current&&o)return;let w=new IntersectionObserver(([I])=>{if(f.current)return;let v=I.isIntersecting;m(v),e?.(v,I),v&&o&&(f.current=!0)},{threshold:t,root:a,rootMargin:i});return w.observe(r),()=>w.disconnect()},[r,t,a,i,o,e]),{ref:s,isIntersecting:c}}var l=u(p()),g="moonui:local-storage";function S(t,a){let[i,o]=(0,l.useState)(()=>{if(typeof window>"u")return a;try{let e=window.localStorage.getItem(t);return e?JSON.parse(e):a}catch(e){return console.error(`Error reading localStorage key "${t}":`,e),a}}),d=e=>{try{let r=e instanceof Function?e(i):e;o(r),typeof window<"u"&&(r===void 0?window.localStorage.removeItem(t):window.localStorage.setItem(t,JSON.stringify(r)),window.dispatchEvent(new CustomEvent(g,{detail:{key:t,value:r}})))}catch(r){console.error(`Error setting localStorage key "${t}":`,r)}};return(0,l.useEffect)(()=>{if(typeof window>"u")return;let e=s=>{if(s.key===t&&s.newValue!==null)try{o(JSON.parse(s.newValue))}catch(c){console.error(`Error parsing localStorage value for key "${t}":`,c)}},r=s=>{let c=s.detail;c&&c.key===t&&o(c.value)};return window.addEventListener("storage",e),window.addEventListener(g,r),()=>{window.removeEventListener("storage",e),window.removeEventListener(g,r)}},[t]),[i,d]}export{E as useIntersectionObserver,S as useLocalStorage};
5
+
6
+ if (typeof window !== 'undefined' && !window.React) {
7
+ console.warn('MoonUI: React not found. Please include React and ReactDOM before MoonUI.');
8
+ }
9
+ //# sourceMappingURL=index.global.js.mape,t,r){this.props=e,this.context=t,this.refs=A,this.updater=r||M}var L=x.prototype=new F;L.constructor=x;q(L,d.prototype);L.isPureReactComponent=!0;var V=Array.isArray,z=Object.prototype.hasOwnProperty,k={current:null},B={key:!0,ref:!0,__self:!0,__source:!0};function J(e,t,r){var n,o={},u=null,c=null;if(t!=null)for(n in t.ref!==void 0&&(c=t.ref),t.key!==void 0&&(u=""+t.key),t)z.call(t,n)&&!B.hasOwnProperty(n)&&(o[n]=t[n]);var l=arguments.length-2;if(l===1)o.children=r;else if(1<l){for(var i=Array(l),a=0;a<l;a++)i[a]=arguments[a+2];o.children=i}if(e&&e.defaultProps)for(n in l=e.defaultProps,l)o[n]===void 0&&(o[n]=l[n]);return{$$typeof:y,type:e,key:u,ref:c,props:o,_owner:k.current}}function _e(e,t){return{$$typeof:y,type:e.type,key:t,ref:e.ref,props:e.props,_owner:e._owner}}function C(e){return typeof e=="object"&&e!==null&&e.$$typeof===y}function we(e){var t={"=":"=0",":":"=2"};return"$"+e.replace(/[=:]/g,function(r){return t[r]})}var D=/\/+/g;function R(e,t){return typeof e=="object"&&e!==null&&e.key!=null?we(""+e.key):t.toString(36)}function w(e,t,r,n,o){var u=typeof e;(u==="undefined"||u==="boolean")&&(e=null);var c=!1;if(e===null)c=!0;else switch(u){case"string":case"number":c=!0;break;case"object":switch(e.$$typeof){case y:case ce:c=!0}}if(c)return c=e,o=o(c),e=n===""?"."+R(c,0):n,V(o)?(r="",e!=null&&(r=e.replace(D,"$&/")+"/"),w(o,t,r,"",function(a){return a})):o!=null&&(C(o)&&(o=_e(o,r+(!o.key||c&&c.key===o.key?"":(""+o.key).replace(D,"$&/")+"/")+e)),t.push(o)),1;if(c=0,n=n===""?".":n+":",V(e))for(var l=0;l<e.length;l++){u=e[l];var i=n+R(u,l);c+=w(u,t,r,i,o)}else if(i=he(e),typeof i=="function")for(e=i.call(e),l=0;!(u=e.next()).done;)u=u.value,i=n+R(u,l++),c+=w(u,t,r,i,o);else if(u==="object")throw t=String(e),Error("Objects are not valid as a React child (found: "+(t==="[object Object]"?"object with keys {"+Object.keys(e).join(", ")+"}":t)+"). If you meant to render a collection of children, use an array instead.");return c}function _(e,t,r){if(e==null)return e;var n=[],o=0;return w(e,n,"","",function(u){return t.call(r,u,o++)}),n}function Se(e){if(e._status===-1){var t=e._result;t=t(),t.then(function(r){(e._status===0||e._status===-1)&&(e._status=1,e._result=r)},function(r){(e._status===0||e._status===-1)&&(e._status=2,e._result=r)}),e._status===-1&&(e._status=0,e._result=t)}if(e._status===1)return e._result.default;throw e._result}var f={current:null},S={transition:null},Ee={ReactCurrentDispatcher:f,ReactCurrentBatchConfig:S,ReactCurrentOwner:k};function H(){throw Error("act(...) is not supported in production builds of React.")}s.Children={map:_,forEach:function(e,t,r){_(e,function(){t.apply(this,arguments)},r)},count:function(e){var t=0;return _(e,function(){t++}),t},toArray:function(e){return _(e,function(t){return t})||[]},only:function(e){if(!C(e))throw Error("React.Children.only expected to receive a single React element child.");return e}};s.Component=d;s.Fragment=ie;s.Profiler=fe;s.PureComponent=x;s.StrictMode=le;s.Suspense=ye;s.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=Ee;s.act=H;s.cloneElement=function(e,t,r){if(e==null)throw Error("React.cloneElement(...): The argument must be a React element, but you passed "+e+".");var n=q({},e.props),o=e.key,u=e.ref,c=e._owner;if(t!=null){if(t.ref!==void 0&&(u=t.ref,c=k.current),t.key!==void 0&&(o=""+t.key),e.type&&e.type.defaultProps)var l=e.type.defaultProps;for(i in t)z.call(t,i)&&!B.hasOwnProperty(i)&&(n[i]=t[i]===void 0&&l!==void 0?l[i]:t[i])}var i=arguments.length-2;if(i===1)n.children=r;else if(1<i){l=Array(i);for(var a=0;a<i;a++)l[a]=arguments[a+2];n.children=l}return{$$typeof:y,type:e.type,key:o,ref:u,props:n,_owner:c}};s.createContext=function(e){return e={$$typeof:pe,_currentValue:e,_currentValue2:e,_threadCount:0,Provider:null,Consumer:null,_defaultValue:null,_globalName:null},e.Provider={$$typeof:ae,_context:e},e.Consumer=e};s.createElement=J;s.createFactory=function(e){var t=J.bind(null,e);return t.type=e,t};s.createRef=function(){return{current:null}};s.forwardRef=function(e){return{$$typeof:de,render:e}};s.isValidElement=C;s.lazy=function(e){return{$$typeof:me,_payload:{_status:-1,_result:e},_init:Se}};s.memo=function(e,t){return{$$typeof:ve,type:e,compare:t===void 0?null:t}};s.startTransition=function(e){var t=S.transition;S.transition={};try{e()}finally{S.transition=t}};s.unstable_act=H;s.useCallback=function(e,t){return f.current.useCallback(e,t)};s.useContext=function(e){return f.current.useContext(e)};s.useDebugValue=function(){};s.useDeferredValue=function(e){return f.current.useDeferredValue(e)};s.useEffect=function(e,t){return f.current.useEffect(e,t)};s.useId=function(){return f.current.useId()};s.useImperativeHandle=function(e,t,r){return f.current.useImperativeHandle(e,t,r)};s.useInsertionEffect=function(e,t){return f.current.useInsertionEffect(e,t)};s.useLayoutEffect=function(e,t){return f.current.useLayoutEffect(e,t)};s.useMemo=function(e,t){return f.current.useMemo(e,t)};s.useReducer=function(e,t,r){return f.current.useReducer(e,t,r)};s.useRef=function(e){return f.current.useRef(e)};s.useState=function(e){return f.current.useState(e)};s.useSyncExternalStore=function(e,t,r){return f.current.useSyncExternalStore(e,t,r)};s.useTransition=function(){return f.current.useTransition()};s.version="18.3.1"});var E=m((Te,Y)=>{"use strict";Y.exports=W()});var K=m(b=>{"use strict";var be=E(),ge=Symbol.for("react.element"),Oe=Symbol.for("react.fragment"),Ie=Object.prototype.hasOwnProperty,Re=be.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,xe={key:!0,ref:!0,__self:!0,__source:!0};function G(e,t,r){var n,o={},u=null,c=null;r!==void 0&&(u=""+r),t.key!==void 0&&(u=""+t.key),t.ref!==void 0&&(c=t.ref);for(n in t)Ie.call(t,n)&&!xe.hasOwnProperty(n)&&(o[n]=t[n]);if(e&&e.defaultProps)for(n in t=e.defaultProps,t)o[n]===void 0&&(o[n]=t[n]);return{$$typeof:ge,type:e,key:u,ref:c,props:o,_owner:Re.current}}b.Fragment=Oe;b.jsx=G;b.jsxs=G});var $=m((Ne,Q)=>{"use strict";Q.exports=K()});var ke={};ue(ke,{useIntersectionObserver:()=>X,useLocalStorage:()=>Z});module.exports=se(ke);var p=h(E()),Le=h($());function X({threshold:e=0,root:t=null,rootMargin:r="0%",freezeOnceVisible:n=!1,initialIsIntersecting:o=!1,onChange:u}={}){let[c,l]=p.useState(null),[i,a]=p.useState(o),O=p.useRef(!1);return p.useEffect(()=>{if(!c||!window.IntersectionObserver||O.current&&n)return;let j=new IntersectionObserver(([N])=>{if(O.current)return;let I=N.isIntersecting;a(I),u?.(I,N),I&&n&&(O.current=!0)},{threshold:e,root:t,rootMargin:r});return j.observe(c),()=>j.disconnect()},[c,e,t,r,n,u]),{ref:l,isIntersecting:i}}var g=h(E()),T="moonui:local-storage";function Z(e,t){let[r,n]=(0,g.useState)(()=>{if(typeof window>"u")return t;try{let u=window.localStorage.getItem(e);return u?JSON.parse(u):t}catch(u){return console.error(`Error reading localStorage key "${e}":`,u),t}}),o=u=>{try{let c=u instanceof Function?u(r):u;n(c),typeof window<"u"&&(c===void 0?window.localStorage.removeItem(e):window.localStorage.setItem(e,JSON.stringify(c)),window.dispatchEvent(new CustomEvent(T,{detail:{key:e,value:c}})))}catch(c){console.error(`Error setting localStorage key "${e}":`,c)}};return(0,g.useEffect)(()=>{if(typeof window>"u")return;let u=l=>{if(l.key===e&&l.newValue!==null)try{n(JSON.parse(l.newValue))}catch(i){console.error(`Error parsing localStorage value for key "${e}":`,i)}},c=l=>{let i=l.detail;i&&i.key===e&&n(i.value)};return window.addEventListener("storage",u),window.addEventListener(T,c),()=>{window.removeEventListener("storage",u),window.removeEventListener(T,c)}},[e]),[r,o]}
10
+ /*! Bundled license information:
11
+
12
+ react/cjs/react.production.min.js:
13
+ (**
14
+ * @license React
15
+ * react.production.min.js
16
+ *
17
+ * Copyright (c) Facebook, Inc. and its affiliates.
18
+ *
19
+ * This source code is licensed under the MIT license found in the
20
+ * LICENSE file in the root directory of this source tree.
21
+ *)
22
+
23
+ react/cjs/react-jsx-runtime.production.min.js:
24
+ (**
25
+ * @license React
26
+ * react-jsx-runtime.production.min.js
27
+ *
28
+ * Copyright (c) Facebook, Inc. and its affiliates.
29
+ *
30
+ * This source code is licensed under the MIT license found in the
31
+ * LICENSE file in the root directory of this source tree.
32
+ *)
33
+ */
34
+
35
+ if (typeof window !== 'undefined' && !window.React) {
36
+ console.warn('MoonUI: React not found. Please include React and ReactDOM before MoonUI.');
37
+ }
38
+ //# sourceMappingURL=index.global.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/use-intersection-observer.tsx","../../src/use-local-storage.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nexport interface UseIntersectionObserverOptions extends IntersectionObserverInit {\n freezeOnceVisible?: boolean;\n initialIsIntersecting?: boolean;\n onChange?: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void;\n}\n\nexport function useIntersectionObserver({\n threshold = 0,\n root = null,\n rootMargin = \"0%\",\n freezeOnceVisible = false,\n initialIsIntersecting = false,\n onChange,\n}: UseIntersectionObserverOptions = {}) {\n const [ref, setRef] = React.useState<Element | null>(null);\n const [isIntersecting, setIsIntersecting] = React.useState(initialIsIntersecting);\n const frozen = React.useRef(false);\n\n React.useEffect(() => {\n if (!ref || !window.IntersectionObserver || (frozen.current && freezeOnceVisible)) {\n return;\n }\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n // freezeOnceVisible: bir kez görünür olduktan sonra state DONMALI.\n // frozen guard'ı yalnızca yukarıdaki effect girişinde vardı; effect'in\n // deps'i değişmediği için yeniden koşmuyor, dolayısıyla guard hiç\n // devreye girmiyordu. Canlı observer bir sonraki entry'de\n // setIsIntersecting(false) çağırıp donmayı bozuyordu.\n if (frozen.current) {\n return;\n }\n\n const isElementIntersecting = entry.isIntersecting;\n setIsIntersecting(isElementIntersecting);\n onChange?.(isElementIntersecting, entry);\n\n if (isElementIntersecting && freezeOnceVisible) {\n frozen.current = true;\n }\n },\n { threshold, root, rootMargin }\n );\n\n observer.observe(ref);\n\n return () => observer.disconnect();\n }, [ref, threshold, root, rootMargin, freezeOnceVisible, onChange]);\n\n return { ref: setRef, isIntersecting };\n}\n\n// Lazy load component wrapper\ninterface LazyLoadProps {\n children: React.ReactNode;\n placeholder?: React.ReactNode;\n threshold?: number;\n rootMargin?: string;\n className?: string;\n}\n\nexport function LazyLoad({\n children,\n placeholder = <div className=\"h-20 bg-muted animate-pulse rounded\" />,\n threshold = 0.1,\n rootMargin = \"50px\",\n className,\n}: LazyLoadProps) {\n const { ref, isIntersecting } = useIntersectionObserver({\n threshold,\n rootMargin,\n freezeOnceVisible: true,\n });\n\n return (\n <div ref={ref} className={className}>\n {isIntersecting ? children : placeholder}\n </div>\n );\n}\n\n// Image lazy loading hook\nexport function useLazyImageLoading(\n imageSrc: string,\n options?: UseIntersectionObserverOptions\n) {\n const [src, setSrc] = React.useState<string | undefined>(undefined);\n const { ref, isIntersecting } = useIntersectionObserver({\n threshold: 0.01,\n rootMargin: \"50px\",\n ...options,\n });\n\n React.useEffect(() => {\n if (isIntersecting && imageSrc) {\n const img = new Image();\n img.src = imageSrc;\n img.onload = () => setSrc(imageSrc);\n }\n }, [isIntersecting, imageSrc]);\n\n return { ref, src, isLoaded: !!src };\n}\n\n// Viewport visibility hook\nexport function useIsInViewport(options?: UseIntersectionObserverOptions) {\n return useIntersectionObserver(options);\n}\n\n// Multiple elements observer\nexport function useMultipleIntersectionObserver(\n options: UseIntersectionObserverOptions = {}\n) {\n const [elements, setElements] = React.useState<Map<Element, boolean>>(new Map());\n const observer = React.useRef<IntersectionObserver | null>(null);\n\n React.useEffect(() => {\n if (!window.IntersectionObserver) return;\n\n observer.current = new IntersectionObserver(\n (entries) => {\n setElements((prev) => {\n const next = new Map(prev);\n entries.forEach((entry) => {\n next.set(entry.target, entry.isIntersecting);\n });\n return next;\n });\n },\n {\n threshold: options.threshold || 0,\n root: options.root || null,\n rootMargin: options.rootMargin || \"0%\",\n }\n );\n\n return () => observer.current?.disconnect();\n }, [options.threshold, options.root, options.rootMargin]);\n\n const observe = React.useCallback((element: Element | null) => {\n if (element && observer.current) {\n observer.current.observe(element);\n }\n }, []);\n\n const unobserve = React.useCallback((element: Element | null) => {\n if (element && observer.current) {\n observer.current.unobserve(element);\n setElements((prev) => {\n const next = new Map(prev);\n next.delete(element);\n return next;\n });\n }\n }, []);\n\n return { observe, unobserve, elements };\n}","import { useState, useEffect } from 'react'\n\n/**\n * Aynı sekmedeki diğer hook instance'larını haberdar eden özel olay.\n *\n * Native `storage` olayı YALNIZCA diğer sekme/pencerelerde tetiklenir — değeri\n * yazan dokümanda hiç tetiklenmez. Bu yüzden aynı sayfada `useLocalStorage(\"k\")`\n * kullanan iki component birbirinden habersiz kalıyor, biri güncellenince\n * diğeri bayat değeri göstermeye devam ediyordu.\n */\nconst LOCAL_SYNC_EVENT = 'moonui:local-storage'\n\ninterface LocalSyncDetail {\n key: string\n value: unknown\n}\n\n// Local storage hook with error handling and SSR support\nexport function useLocalStorage<T>(\n key: string,\n initialValue: T\n): [T, (value: T | ((val: T) => T)) => void] {\n // State to store our value\n // Pass initial state function to useState so logic is only executed once\n const [storedValue, setStoredValue] = useState<T>(() => {\n if (typeof window === 'undefined') {\n return initialValue\n }\n \n try {\n // Get from local storage by key\n const item = window.localStorage.getItem(key)\n // Parse stored json or if none return initialValue\n return item ? JSON.parse(item) : initialValue\n } catch (error) {\n // If error also return initialValue\n console.error(`Error reading localStorage key \"${key}\":`, error)\n return initialValue\n }\n })\n\n // Return a wrapped version of useState's setter function that persists the new value to localStorage\n const setValue = (value: T | ((val: T) => T)) => {\n try {\n // Allow value to be a function so we have the same API as useState\n const valueToStore = value instanceof Function ? value(storedValue) : value\n \n // Save state\n setStoredValue(valueToStore)\n \n // Save to local storage\n if (typeof window !== 'undefined') {\n if (valueToStore === undefined) {\n window.localStorage.removeItem(key)\n } else {\n window.localStorage.setItem(key, JSON.stringify(valueToStore))\n }\n\n // Aynı dokümandaki diğer instance'ları bilgilendir (bkz. LOCAL_SYNC_EVENT)\n window.dispatchEvent(\n new CustomEvent<LocalSyncDetail>(LOCAL_SYNC_EVENT, {\n detail: { key, value: valueToStore },\n })\n )\n }\n } catch (error) {\n // A more advanced implementation would handle the error case\n console.error(`Error setting localStorage key \"${key}\":`, error)\n }\n }\n\n // Listen for changes to localStorage from other tabs/windows\n useEffect(() => {\n if (typeof window === 'undefined') {\n return\n }\n\n const handleStorageChange = (e: StorageEvent) => {\n if (e.key === key && e.newValue !== null) {\n try {\n setStoredValue(JSON.parse(e.newValue))\n } catch (error) {\n console.error(`Error parsing localStorage value for key \"${key}\":`, error)\n }\n }\n }\n\n // Aynı sekmedeki kardeş instance'lar\n const handleLocalSync = (e: Event) => {\n const detail = (e as CustomEvent<LocalSyncDetail>).detail\n if (detail && detail.key === key) {\n setStoredValue(detail.value as T)\n }\n }\n\n window.addEventListener('storage', handleStorageChange)\n window.addEventListener(LOCAL_SYNC_EVENT, handleLocalSync)\n return () => {\n window.removeEventListener('storage', handleStorageChange)\n window.removeEventListener(LOCAL_SYNC_EVENT, handleLocalSync)\n }\n }, [key])\n\n return [storedValue, setValue]\n}"],"mappings":";2EAEA,IAAAA,EAAuB,OAkEPC,EAAA,OA1DT,SAASC,EAAwB,CACtC,UAAAC,EAAY,EACZ,KAAAC,EAAO,KACP,WAAAC,EAAa,KACb,kBAAAC,EAAoB,GACpB,sBAAAC,EAAwB,GACxB,SAAAC,CACF,EAAoC,CAAC,EAAG,CACtC,GAAM,CAACC,EAAKC,CAAM,EAAU,WAAyB,IAAI,EACnD,CAACC,EAAgBC,CAAiB,EAAU,WAASL,CAAqB,EAC1EM,EAAe,SAAO,EAAK,EAEjC,OAAM,YAAU,IAAM,CACpB,GAAI,CAACJ,GAAO,CAAC,OAAO,sBAAyBI,EAAO,SAAWP,EAC7D,OAGF,IAAMQ,EAAW,IAAI,qBACnB,CAAC,CAACC,CAAK,IAAM,CAMX,GAAIF,EAAO,QACT,OAGF,IAAMG,EAAwBD,EAAM,eACpCH,EAAkBI,CAAqB,EACvCR,IAAWQ,EAAuBD,CAAK,EAEnCC,GAAyBV,IAC3BO,EAAO,QAAU,GAErB,EACA,CAAE,UAAAV,EAAW,KAAAC,EAAM,WAAAC,CAAW,CAChC,EAEA,OAAAS,EAAS,QAAQL,CAAG,EAEb,IAAMK,EAAS,WAAW,CACnC,EAAG,CAACL,EAAKN,EAAWC,EAAMC,EAAYC,EAAmBE,CAAQ,CAAC,EAE3D,CAAE,IAAKE,EAAQ,eAAAC,CAAe,CACvC,CCvDA,IAAAM,EAAoC,OAU9BC,EAAmB,uBAQlB,SAASC,EACdC,EACAC,EAC2C,CAG3C,GAAM,CAACC,EAAaC,CAAc,KAAI,YAAY,IAAM,CACtD,GAAI,OAAO,OAAW,IACpB,OAAOF,EAGT,GAAI,CAEF,IAAMG,EAAO,OAAO,aAAa,QAAQJ,CAAG,EAE5C,OAAOI,EAAO,KAAK,MAAMA,CAAI,EAAIH,CACnC,OAASI,EAAO,CAEd,eAAQ,MAAM,mCAAmCL,CAAG,KAAMK,CAAK,EACxDJ,CACT,CACF,CAAC,EAGKK,EAAYC,GAA+B,CAC/C,GAAI,CAEF,IAAMC,EAAeD,aAAiB,SAAWA,EAAML,CAAW,EAAIK,EAGtEJ,EAAeK,CAAY,EAGvB,OAAO,OAAW,MAChBA,IAAiB,OACnB,OAAO,aAAa,WAAWR,CAAG,EAElC,OAAO,aAAa,QAAQA,EAAK,KAAK,UAAUQ,CAAY,CAAC,EAI/D,OAAO,cACL,IAAI,YAA6BV,EAAkB,CACjD,OAAQ,CAAE,IAAAE,EAAK,MAAOQ,CAAa,CACrC,CAAC,CACH,EAEJ,OAASH,EAAO,CAEd,QAAQ,MAAM,mCAAmCL,CAAG,KAAMK,CAAK,CACjE,CACF,EAGA,sBAAU,IAAM,CACd,GAAI,OAAO,OAAW,IACpB,OAGF,IAAMI,EAAuBC,GAAoB,CAC/C,GAAIA,EAAE,MAAQV,GAAOU,EAAE,WAAa,KAClC,GAAI,CACFP,EAAe,KAAK,MAAMO,EAAE,QAAQ,CAAC,CACvC,OAASL,EAAO,CACd,QAAQ,MAAM,6CAA6CL,CAAG,KAAMK,CAAK,CAC3E,CAEJ,EAGMM,EAAmBD,GAAa,CACpC,IAAME,EAAUF,EAAmC,OAC/CE,GAAUA,EAAO,MAAQZ,GAC3BG,EAAeS,EAAO,KAAU,CAEpC,EAEA,cAAO,iBAAiB,UAAWH,CAAmB,EACtD,OAAO,iBAAiBX,EAAkBa,CAAe,EAClD,IAAM,CACX,OAAO,oBAAoB,UAAWF,CAAmB,EACzD,OAAO,oBAAoBX,EAAkBa,CAAe,CAC9D,CACF,EAAG,CAACX,CAAG,CAAC,EAED,CAACE,EAAaI,CAAQ,CAC/B","names":["React","import_jsx_runtime","useIntersectionObserver","threshold","root","rootMargin","freezeOnceVisible","initialIsIntersecting","onChange","ref","setRef","isIntersecting","setIsIntersecting","frozen","observer","entry","isElementIntersecting","import_react","LOCAL_SYNC_EVENT","useLocalStorage","key","initialValue","storedValue","setStoredValue","item","error","setValue","value","valueToStore","handleStorageChange","e","handleLocalSync","detail"]}
@@ -0,0 +1,128 @@
1
+ 'use client';
2
+
3
+ 'use strict';
4
+
5
+ var React = require('react');
6
+ require('react/jsx-runtime');
7
+
8
+ function _interopNamespace(e) {
9
+ if (e && e.__esModule) return e;
10
+ var n = Object.create(null);
11
+ if (e) {
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: function () { return e[k]; }
18
+ });
19
+ }
20
+ });
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
27
+
28
+ function useIntersectionObserver({
29
+ threshold = 0,
30
+ root = null,
31
+ rootMargin = "0%",
32
+ freezeOnceVisible = false,
33
+ initialIsIntersecting = false,
34
+ onChange
35
+ } = {}) {
36
+ const [ref, setRef] = React__namespace.useState(null);
37
+ const [isIntersecting, setIsIntersecting] = React__namespace.useState(initialIsIntersecting);
38
+ const frozen = React__namespace.useRef(false);
39
+ React__namespace.useEffect(() => {
40
+ if (!ref || !window.IntersectionObserver || frozen.current && freezeOnceVisible) {
41
+ return;
42
+ }
43
+ const observer = new IntersectionObserver(
44
+ ([entry]) => {
45
+ if (frozen.current) {
46
+ return;
47
+ }
48
+ const isElementIntersecting = entry.isIntersecting;
49
+ setIsIntersecting(isElementIntersecting);
50
+ onChange?.(isElementIntersecting, entry);
51
+ if (isElementIntersecting && freezeOnceVisible) {
52
+ frozen.current = true;
53
+ }
54
+ },
55
+ { threshold, root, rootMargin }
56
+ );
57
+ observer.observe(ref);
58
+ return () => observer.disconnect();
59
+ }, [ref, threshold, root, rootMargin, freezeOnceVisible, onChange]);
60
+ return { ref: setRef, isIntersecting };
61
+ }
62
+ var LOCAL_SYNC_EVENT = "moonui:local-storage";
63
+ function useLocalStorage(key, initialValue) {
64
+ const [storedValue, setStoredValue] = React.useState(() => {
65
+ if (typeof window === "undefined") {
66
+ return initialValue;
67
+ }
68
+ try {
69
+ const item = window.localStorage.getItem(key);
70
+ return item ? JSON.parse(item) : initialValue;
71
+ } catch (error) {
72
+ console.error(`Error reading localStorage key "${key}":`, error);
73
+ return initialValue;
74
+ }
75
+ });
76
+ const setValue = (value) => {
77
+ try {
78
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
79
+ setStoredValue(valueToStore);
80
+ if (typeof window !== "undefined") {
81
+ if (valueToStore === void 0) {
82
+ window.localStorage.removeItem(key);
83
+ } else {
84
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
85
+ }
86
+ window.dispatchEvent(
87
+ new CustomEvent(LOCAL_SYNC_EVENT, {
88
+ detail: { key, value: valueToStore }
89
+ })
90
+ );
91
+ }
92
+ } catch (error) {
93
+ console.error(`Error setting localStorage key "${key}":`, error);
94
+ }
95
+ };
96
+ React.useEffect(() => {
97
+ if (typeof window === "undefined") {
98
+ return;
99
+ }
100
+ const handleStorageChange = (e) => {
101
+ if (e.key === key && e.newValue !== null) {
102
+ try {
103
+ setStoredValue(JSON.parse(e.newValue));
104
+ } catch (error) {
105
+ console.error(`Error parsing localStorage value for key "${key}":`, error);
106
+ }
107
+ }
108
+ };
109
+ const handleLocalSync = (e) => {
110
+ const detail = e.detail;
111
+ if (detail && detail.key === key) {
112
+ setStoredValue(detail.value);
113
+ }
114
+ };
115
+ window.addEventListener("storage", handleStorageChange);
116
+ window.addEventListener(LOCAL_SYNC_EVENT, handleLocalSync);
117
+ return () => {
118
+ window.removeEventListener("storage", handleStorageChange);
119
+ window.removeEventListener(LOCAL_SYNC_EVENT, handleLocalSync);
120
+ };
121
+ }, [key]);
122
+ return [storedValue, setValue];
123
+ }
124
+
125
+ exports.useIntersectionObserver = useIntersectionObserver;
126
+ exports.useLocalStorage = useLocalStorage;
127
+ //# sourceMappingURL=out.js.map
128
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/use-intersection-observer.tsx","../../src/use-local-storage.tsx"],"names":["useState","useEffect"],"mappings":"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","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nexport interface UseIntersectionObserverOptions extends IntersectionObserverInit {\n freezeOnceVisible?: boolean;\n initialIsIntersecting?: boolean;\n onChange?: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void;\n}\n\nexport function useIntersectionObserver({\n threshold = 0,\n root = null,\n rootMargin = \"0%\",\n freezeOnceVisible = false,\n initialIsIntersecting = false,\n onChange,\n}: UseIntersectionObserverOptions = {}) {\n const [ref, setRef] = React.useState<Element | null>(null);\n const [isIntersecting, setIsIntersecting] = React.useState(initialIsIntersecting);\n const frozen = React.useRef(false);\n\n React.useEffect(() => {\n if (!ref || !window.IntersectionObserver || (frozen.current && freezeOnceVisible)) {\n return;\n }\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n // freezeOnceVisible: bir kez görünür olduktan sonra state DONMALI.\n // frozen guard'ı yalnızca yukarıdaki effect girişinde vardı; effect'in\n // deps'i değişmediği için yeniden koşmuyor, dolayısıyla guard hiç\n // devreye girmiyordu. Canlı observer bir sonraki entry'de\n // setIsIntersecting(false) çağırıp donmayı bozuyordu.\n if (frozen.current) {\n return;\n }\n\n const isElementIntersecting = entry.isIntersecting;\n setIsIntersecting(isElementIntersecting);\n onChange?.(isElementIntersecting, entry);\n\n if (isElementIntersecting && freezeOnceVisible) {\n frozen.current = true;\n }\n },\n { threshold, root, rootMargin }\n );\n\n observer.observe(ref);\n\n return () => observer.disconnect();\n }, [ref, threshold, root, rootMargin, freezeOnceVisible, onChange]);\n\n return { ref: setRef, isIntersecting };\n}\n\n// Lazy load component wrapper\ninterface LazyLoadProps {\n children: React.ReactNode;\n placeholder?: React.ReactNode;\n threshold?: number;\n rootMargin?: string;\n className?: string;\n}\n\nexport function LazyLoad({\n children,\n placeholder = <div className=\"h-20 bg-muted animate-pulse rounded\" />,\n threshold = 0.1,\n rootMargin = \"50px\",\n className,\n}: LazyLoadProps) {\n const { ref, isIntersecting } = useIntersectionObserver({\n threshold,\n rootMargin,\n freezeOnceVisible: true,\n });\n\n return (\n <div ref={ref} className={className}>\n {isIntersecting ? children : placeholder}\n </div>\n );\n}\n\n// Image lazy loading hook\nexport function useLazyImageLoading(\n imageSrc: string,\n options?: UseIntersectionObserverOptions\n) {\n const [src, setSrc] = React.useState<string | undefined>(undefined);\n const { ref, isIntersecting } = useIntersectionObserver({\n threshold: 0.01,\n rootMargin: \"50px\",\n ...options,\n });\n\n React.useEffect(() => {\n if (isIntersecting && imageSrc) {\n const img = new Image();\n img.src = imageSrc;\n img.onload = () => setSrc(imageSrc);\n }\n }, [isIntersecting, imageSrc]);\n\n return { ref, src, isLoaded: !!src };\n}\n\n// Viewport visibility hook\nexport function useIsInViewport(options?: UseIntersectionObserverOptions) {\n return useIntersectionObserver(options);\n}\n\n// Multiple elements observer\nexport function useMultipleIntersectionObserver(\n options: UseIntersectionObserverOptions = {}\n) {\n const [elements, setElements] = React.useState<Map<Element, boolean>>(new Map());\n const observer = React.useRef<IntersectionObserver | null>(null);\n\n React.useEffect(() => {\n if (!window.IntersectionObserver) return;\n\n observer.current = new IntersectionObserver(\n (entries) => {\n setElements((prev) => {\n const next = new Map(prev);\n entries.forEach((entry) => {\n next.set(entry.target, entry.isIntersecting);\n });\n return next;\n });\n },\n {\n threshold: options.threshold || 0,\n root: options.root || null,\n rootMargin: options.rootMargin || \"0%\",\n }\n );\n\n return () => observer.current?.disconnect();\n }, [options.threshold, options.root, options.rootMargin]);\n\n const observe = React.useCallback((element: Element | null) => {\n if (element && observer.current) {\n observer.current.observe(element);\n }\n }, []);\n\n const unobserve = React.useCallback((element: Element | null) => {\n if (element && observer.current) {\n observer.current.unobserve(element);\n setElements((prev) => {\n const next = new Map(prev);\n next.delete(element);\n return next;\n });\n }\n }, []);\n\n return { observe, unobserve, elements };\n}","import { useState, useEffect } from 'react'\n\n/**\n * Aynı sekmedeki diğer hook instance'larını haberdar eden özel olay.\n *\n * Native `storage` olayı YALNIZCA diğer sekme/pencerelerde tetiklenir — değeri\n * yazan dokümanda hiç tetiklenmez. Bu yüzden aynı sayfada `useLocalStorage(\"k\")`\n * kullanan iki component birbirinden habersiz kalıyor, biri güncellenince\n * diğeri bayat değeri göstermeye devam ediyordu.\n */\nconst LOCAL_SYNC_EVENT = 'moonui:local-storage'\n\ninterface LocalSyncDetail {\n key: string\n value: unknown\n}\n\n// Local storage hook with error handling and SSR support\nexport function useLocalStorage<T>(\n key: string,\n initialValue: T\n): [T, (value: T | ((val: T) => T)) => void] {\n // State to store our value\n // Pass initial state function to useState so logic is only executed once\n const [storedValue, setStoredValue] = useState<T>(() => {\n if (typeof window === 'undefined') {\n return initialValue\n }\n \n try {\n // Get from local storage by key\n const item = window.localStorage.getItem(key)\n // Parse stored json or if none return initialValue\n return item ? JSON.parse(item) : initialValue\n } catch (error) {\n // If error also return initialValue\n console.error(`Error reading localStorage key \"${key}\":`, error)\n return initialValue\n }\n })\n\n // Return a wrapped version of useState's setter function that persists the new value to localStorage\n const setValue = (value: T | ((val: T) => T)) => {\n try {\n // Allow value to be a function so we have the same API as useState\n const valueToStore = value instanceof Function ? value(storedValue) : value\n \n // Save state\n setStoredValue(valueToStore)\n \n // Save to local storage\n if (typeof window !== 'undefined') {\n if (valueToStore === undefined) {\n window.localStorage.removeItem(key)\n } else {\n window.localStorage.setItem(key, JSON.stringify(valueToStore))\n }\n\n // Aynı dokümandaki diğer instance'ları bilgilendir (bkz. LOCAL_SYNC_EVENT)\n window.dispatchEvent(\n new CustomEvent<LocalSyncDetail>(LOCAL_SYNC_EVENT, {\n detail: { key, value: valueToStore },\n })\n )\n }\n } catch (error) {\n // A more advanced implementation would handle the error case\n console.error(`Error setting localStorage key \"${key}\":`, error)\n }\n }\n\n // Listen for changes to localStorage from other tabs/windows\n useEffect(() => {\n if (typeof window === 'undefined') {\n return\n }\n\n const handleStorageChange = (e: StorageEvent) => {\n if (e.key === key && e.newValue !== null) {\n try {\n setStoredValue(JSON.parse(e.newValue))\n } catch (error) {\n console.error(`Error parsing localStorage value for key \"${key}\":`, error)\n }\n }\n }\n\n // Aynı sekmedeki kardeş instance'lar\n const handleLocalSync = (e: Event) => {\n const detail = (e as CustomEvent<LocalSyncDetail>).detail\n if (detail && detail.key === key) {\n setStoredValue(detail.value as T)\n }\n }\n\n window.addEventListener('storage', handleStorageChange)\n window.addEventListener(LOCAL_SYNC_EVENT, handleLocalSync)\n return () => {\n window.removeEventListener('storage', handleStorageChange)\n window.removeEventListener(LOCAL_SYNC_EVENT, handleLocalSync)\n }\n }, [key])\n\n return [storedValue, setValue]\n}"]}
@@ -0,0 +1,106 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { useState, useEffect } from 'react';
5
+ import 'react/jsx-runtime';
6
+
7
+ function useIntersectionObserver({
8
+ threshold = 0,
9
+ root = null,
10
+ rootMargin = "0%",
11
+ freezeOnceVisible = false,
12
+ initialIsIntersecting = false,
13
+ onChange
14
+ } = {}) {
15
+ const [ref, setRef] = React.useState(null);
16
+ const [isIntersecting, setIsIntersecting] = React.useState(initialIsIntersecting);
17
+ const frozen = React.useRef(false);
18
+ React.useEffect(() => {
19
+ if (!ref || !window.IntersectionObserver || frozen.current && freezeOnceVisible) {
20
+ return;
21
+ }
22
+ const observer = new IntersectionObserver(
23
+ ([entry]) => {
24
+ if (frozen.current) {
25
+ return;
26
+ }
27
+ const isElementIntersecting = entry.isIntersecting;
28
+ setIsIntersecting(isElementIntersecting);
29
+ onChange?.(isElementIntersecting, entry);
30
+ if (isElementIntersecting && freezeOnceVisible) {
31
+ frozen.current = true;
32
+ }
33
+ },
34
+ { threshold, root, rootMargin }
35
+ );
36
+ observer.observe(ref);
37
+ return () => observer.disconnect();
38
+ }, [ref, threshold, root, rootMargin, freezeOnceVisible, onChange]);
39
+ return { ref: setRef, isIntersecting };
40
+ }
41
+ var LOCAL_SYNC_EVENT = "moonui:local-storage";
42
+ function useLocalStorage(key, initialValue) {
43
+ const [storedValue, setStoredValue] = useState(() => {
44
+ if (typeof window === "undefined") {
45
+ return initialValue;
46
+ }
47
+ try {
48
+ const item = window.localStorage.getItem(key);
49
+ return item ? JSON.parse(item) : initialValue;
50
+ } catch (error) {
51
+ console.error(`Error reading localStorage key "${key}":`, error);
52
+ return initialValue;
53
+ }
54
+ });
55
+ const setValue = (value) => {
56
+ try {
57
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
58
+ setStoredValue(valueToStore);
59
+ if (typeof window !== "undefined") {
60
+ if (valueToStore === void 0) {
61
+ window.localStorage.removeItem(key);
62
+ } else {
63
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
64
+ }
65
+ window.dispatchEvent(
66
+ new CustomEvent(LOCAL_SYNC_EVENT, {
67
+ detail: { key, value: valueToStore }
68
+ })
69
+ );
70
+ }
71
+ } catch (error) {
72
+ console.error(`Error setting localStorage key "${key}":`, error);
73
+ }
74
+ };
75
+ useEffect(() => {
76
+ if (typeof window === "undefined") {
77
+ return;
78
+ }
79
+ const handleStorageChange = (e) => {
80
+ if (e.key === key && e.newValue !== null) {
81
+ try {
82
+ setStoredValue(JSON.parse(e.newValue));
83
+ } catch (error) {
84
+ console.error(`Error parsing localStorage value for key "${key}":`, error);
85
+ }
86
+ }
87
+ };
88
+ const handleLocalSync = (e) => {
89
+ const detail = e.detail;
90
+ if (detail && detail.key === key) {
91
+ setStoredValue(detail.value);
92
+ }
93
+ };
94
+ window.addEventListener("storage", handleStorageChange);
95
+ window.addEventListener(LOCAL_SYNC_EVENT, handleLocalSync);
96
+ return () => {
97
+ window.removeEventListener("storage", handleStorageChange);
98
+ window.removeEventListener(LOCAL_SYNC_EVENT, handleLocalSync);
99
+ };
100
+ }, [key]);
101
+ return [storedValue, setValue];
102
+ }
103
+
104
+ export { useIntersectionObserver, useLocalStorage };
105
+ //# sourceMappingURL=out.js.map
106
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/use-intersection-observer.tsx","../../src/use-local-storage.tsx"],"names":["useState","useEffect"],"mappings":"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","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nexport interface UseIntersectionObserverOptions extends IntersectionObserverInit {\n freezeOnceVisible?: boolean;\n initialIsIntersecting?: boolean;\n onChange?: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void;\n}\n\nexport function useIntersectionObserver({\n threshold = 0,\n root = null,\n rootMargin = \"0%\",\n freezeOnceVisible = false,\n initialIsIntersecting = false,\n onChange,\n}: UseIntersectionObserverOptions = {}) {\n const [ref, setRef] = React.useState<Element | null>(null);\n const [isIntersecting, setIsIntersecting] = React.useState(initialIsIntersecting);\n const frozen = React.useRef(false);\n\n React.useEffect(() => {\n if (!ref || !window.IntersectionObserver || (frozen.current && freezeOnceVisible)) {\n return;\n }\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n // freezeOnceVisible: bir kez görünür olduktan sonra state DONMALI.\n // frozen guard'ı yalnızca yukarıdaki effect girişinde vardı; effect'in\n // deps'i değişmediği için yeniden koşmuyor, dolayısıyla guard hiç\n // devreye girmiyordu. Canlı observer bir sonraki entry'de\n // setIsIntersecting(false) çağırıp donmayı bozuyordu.\n if (frozen.current) {\n return;\n }\n\n const isElementIntersecting = entry.isIntersecting;\n setIsIntersecting(isElementIntersecting);\n onChange?.(isElementIntersecting, entry);\n\n if (isElementIntersecting && freezeOnceVisible) {\n frozen.current = true;\n }\n },\n { threshold, root, rootMargin }\n );\n\n observer.observe(ref);\n\n return () => observer.disconnect();\n }, [ref, threshold, root, rootMargin, freezeOnceVisible, onChange]);\n\n return { ref: setRef, isIntersecting };\n}\n\n// Lazy load component wrapper\ninterface LazyLoadProps {\n children: React.ReactNode;\n placeholder?: React.ReactNode;\n threshold?: number;\n rootMargin?: string;\n className?: string;\n}\n\nexport function LazyLoad({\n children,\n placeholder = <div className=\"h-20 bg-muted animate-pulse rounded\" />,\n threshold = 0.1,\n rootMargin = \"50px\",\n className,\n}: LazyLoadProps) {\n const { ref, isIntersecting } = useIntersectionObserver({\n threshold,\n rootMargin,\n freezeOnceVisible: true,\n });\n\n return (\n <div ref={ref} className={className}>\n {isIntersecting ? children : placeholder}\n </div>\n );\n}\n\n// Image lazy loading hook\nexport function useLazyImageLoading(\n imageSrc: string,\n options?: UseIntersectionObserverOptions\n) {\n const [src, setSrc] = React.useState<string | undefined>(undefined);\n const { ref, isIntersecting } = useIntersectionObserver({\n threshold: 0.01,\n rootMargin: \"50px\",\n ...options,\n });\n\n React.useEffect(() => {\n if (isIntersecting && imageSrc) {\n const img = new Image();\n img.src = imageSrc;\n img.onload = () => setSrc(imageSrc);\n }\n }, [isIntersecting, imageSrc]);\n\n return { ref, src, isLoaded: !!src };\n}\n\n// Viewport visibility hook\nexport function useIsInViewport(options?: UseIntersectionObserverOptions) {\n return useIntersectionObserver(options);\n}\n\n// Multiple elements observer\nexport function useMultipleIntersectionObserver(\n options: UseIntersectionObserverOptions = {}\n) {\n const [elements, setElements] = React.useState<Map<Element, boolean>>(new Map());\n const observer = React.useRef<IntersectionObserver | null>(null);\n\n React.useEffect(() => {\n if (!window.IntersectionObserver) return;\n\n observer.current = new IntersectionObserver(\n (entries) => {\n setElements((prev) => {\n const next = new Map(prev);\n entries.forEach((entry) => {\n next.set(entry.target, entry.isIntersecting);\n });\n return next;\n });\n },\n {\n threshold: options.threshold || 0,\n root: options.root || null,\n rootMargin: options.rootMargin || \"0%\",\n }\n );\n\n return () => observer.current?.disconnect();\n }, [options.threshold, options.root, options.rootMargin]);\n\n const observe = React.useCallback((element: Element | null) => {\n if (element && observer.current) {\n observer.current.observe(element);\n }\n }, []);\n\n const unobserve = React.useCallback((element: Element | null) => {\n if (element && observer.current) {\n observer.current.unobserve(element);\n setElements((prev) => {\n const next = new Map(prev);\n next.delete(element);\n return next;\n });\n }\n }, []);\n\n return { observe, unobserve, elements };\n}","import { useState, useEffect } from 'react'\n\n/**\n * Aynı sekmedeki diğer hook instance'larını haberdar eden özel olay.\n *\n * Native `storage` olayı YALNIZCA diğer sekme/pencerelerde tetiklenir — değeri\n * yazan dokümanda hiç tetiklenmez. Bu yüzden aynı sayfada `useLocalStorage(\"k\")`\n * kullanan iki component birbirinden habersiz kalıyor, biri güncellenince\n * diğeri bayat değeri göstermeye devam ediyordu.\n */\nconst LOCAL_SYNC_EVENT = 'moonui:local-storage'\n\ninterface LocalSyncDetail {\n key: string\n value: unknown\n}\n\n// Local storage hook with error handling and SSR support\nexport function useLocalStorage<T>(\n key: string,\n initialValue: T\n): [T, (value: T | ((val: T) => T)) => void] {\n // State to store our value\n // Pass initial state function to useState so logic is only executed once\n const [storedValue, setStoredValue] = useState<T>(() => {\n if (typeof window === 'undefined') {\n return initialValue\n }\n \n try {\n // Get from local storage by key\n const item = window.localStorage.getItem(key)\n // Parse stored json or if none return initialValue\n return item ? JSON.parse(item) : initialValue\n } catch (error) {\n // If error also return initialValue\n console.error(`Error reading localStorage key \"${key}\":`, error)\n return initialValue\n }\n })\n\n // Return a wrapped version of useState's setter function that persists the new value to localStorage\n const setValue = (value: T | ((val: T) => T)) => {\n try {\n // Allow value to be a function so we have the same API as useState\n const valueToStore = value instanceof Function ? value(storedValue) : value\n \n // Save state\n setStoredValue(valueToStore)\n \n // Save to local storage\n if (typeof window !== 'undefined') {\n if (valueToStore === undefined) {\n window.localStorage.removeItem(key)\n } else {\n window.localStorage.setItem(key, JSON.stringify(valueToStore))\n }\n\n // Aynı dokümandaki diğer instance'ları bilgilendir (bkz. LOCAL_SYNC_EVENT)\n window.dispatchEvent(\n new CustomEvent<LocalSyncDetail>(LOCAL_SYNC_EVENT, {\n detail: { key, value: valueToStore },\n })\n )\n }\n } catch (error) {\n // A more advanced implementation would handle the error case\n console.error(`Error setting localStorage key \"${key}\":`, error)\n }\n }\n\n // Listen for changes to localStorage from other tabs/windows\n useEffect(() => {\n if (typeof window === 'undefined') {\n return\n }\n\n const handleStorageChange = (e: StorageEvent) => {\n if (e.key === key && e.newValue !== null) {\n try {\n setStoredValue(JSON.parse(e.newValue))\n } catch (error) {\n console.error(`Error parsing localStorage value for key \"${key}\":`, error)\n }\n }\n }\n\n // Aynı sekmedeki kardeş instance'lar\n const handleLocalSync = (e: Event) => {\n const detail = (e as CustomEvent<LocalSyncDetail>).detail\n if (detail && detail.key === key) {\n setStoredValue(detail.value as T)\n }\n }\n\n window.addEventListener('storage', handleStorageChange)\n window.addEventListener(LOCAL_SYNC_EVENT, handleLocalSync)\n return () => {\n window.removeEventListener('storage', handleStorageChange)\n window.removeEventListener(LOCAL_SYNC_EVENT, handleLocalSync)\n }\n }, [key])\n\n return [storedValue, setValue]\n}"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moontra/moonui",
3
- "version": "5.0.1",
3
+ "version": "5.1.0",
4
4
  "description": "Premium React component library with modern design and customization",
5
5
  "author": "MoonUI",
6
6
  "license": "MIT",
@@ -38,6 +38,11 @@
38
38
  "import": "./dist/lib/theme.mjs",
39
39
  "require": "./dist/lib/theme.js"
40
40
  },
41
+ "./hooks": {
42
+ "types": "./dist/hooks/index.d.ts",
43
+ "import": "./dist/hooks/index.mjs",
44
+ "require": "./dist/hooks/index.js"
45
+ },
41
46
  "./tokens.css": "./src/styles/tokens.css",
42
47
  "./design-system.css": "./src/styles/design-system.css",
43
48
  "./components/*": {
@@ -60,8 +65,8 @@
60
65
  "scripts": {
61
66
  "dev": "tsup src/index.tsx --format esm,cjs --dts --watch --sourcemap",
62
67
  "build": "npm run build:js && npm run build:dts && node scripts/postbuild.js",
63
- "build:js": "tsup src/index.tsx src/lib/theme.tsx --format esm,cjs",
64
- "build:dts": "tsup src/index.tsx src/lib/theme.tsx --format esm,cjs --dts-only",
68
+ "build:js": "tsup src/index.tsx src/lib/theme.tsx src/hooks/index.ts --format esm,cjs",
69
+ "build:dts": "tsup src/index.tsx src/lib/theme.tsx src/hooks/index.ts --format esm,cjs --dts-only",
65
70
  "lint": "eslint \"src/**/*.{ts,tsx}\" --fix",
66
71
  "clean": "rm -rf dist",
67
72
  "prepublishOnly": "npm run clean && npm run build",
@@ -0,0 +1,22 @@
1
+ import * as hooks from '../hooks';
2
+
3
+ /**
4
+ * `@moontra/moonui/hooks` alt-yolunun public sözleşmesi.
5
+ *
6
+ * Bu hook'lar uzun süre `src/` kökünde yetim durdu: dosyalar tarball'a giriyor
7
+ * ama hiçbir index'ten re-export edilmediği için `exports` haritasından
8
+ * erişilemiyorlardı — yani tüketici için YOK hükmündeydiler. Bu test, barrel'ın
9
+ * sessizce boşalmasına veya bir hook'un yanlışlıkla düşmesine karşı kapı.
10
+ */
11
+
12
+ const EXPECTED_EXPORTS = ['useIntersectionObserver', 'useLocalStorage'] as const;
13
+
14
+ describe('@moontra/moonui/hooks export sözleşmesi', () => {
15
+ it.each(EXPECTED_EXPORTS)('%s dışa açık ve fonksiyon', (name) => {
16
+ expect(typeof (hooks as Record<string, unknown>)[name]).toBe('function');
17
+ });
18
+
19
+ it('barrel yalnızca beklenen yüzeyi taşır (kazara genişlemeye karşı)', () => {
20
+ expect(Object.keys(hooks).sort()).toEqual([...EXPECTED_EXPORTS].sort());
21
+ });
22
+ });
@@ -0,0 +1,24 @@
1
+ "use client";
2
+
3
+ /**
4
+ * `@moontra/moonui/hooks` — genel amaçlı React hook'ları.
5
+ *
6
+ * NEDEN AYRI ALT-YOL (`./hooks`), ana barrel değil:
7
+ * `src/index.tsx` sözleşme gereği yalnızca iki bölüm içerir (`./components/ui`
8
+ * re-export'u + `cn`). Hook'ları oraya eklemek o sözleşmeyi bozardı, bu yüzden
9
+ * hook'lar kendi giriş noktasından yayımlanır:
10
+ *
11
+ * import { useLocalStorage } from "@moontra/moonui/hooks"
12
+ *
13
+ * KAPSAM: buraya yalnızca TESTİ OLAN hook'lar alınır. `src/` kökünde henüz
14
+ * dışa açılmamış başka yardımcılar da var (`LazyLoad`, `useLazyImageLoading`,
15
+ * `useIsInViewport`, `useMultipleIntersectionObserver`, `use-scroll-animation`,
16
+ * `use-performance-optimizer` ve `use-toast.ts`'in yetim kopyası). Test
17
+ * kapsamları olmadığı için public API'ye alınmadılar — eklenmeden önce test
18
+ * yazılmalı.
19
+ */
20
+
21
+ export { useIntersectionObserver } from "../use-intersection-observer";
22
+ export type { UseIntersectionObserverOptions } from "../use-intersection-observer";
23
+
24
+ export { useLocalStorage } from "../use-local-storage";
@@ -2,7 +2,7 @@
2
2
 
3
3
  import * as React from "react";
4
4
 
5
- interface UseIntersectionObserverOptions extends IntersectionObserverInit {
5
+ export interface UseIntersectionObserverOptions extends IntersectionObserverInit {
6
6
  freezeOnceVisible?: boolean;
7
7
  initialIsIntersecting?: boolean;
8
8
  onChange?: (isIntersecting: boolean, entry: IntersectionObserverEntry) => void;