Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
package.react-utils.d.ts Maven / Gradle / Ivy
Go to download
A package encapsulating common code across neeto projects including initializers, utility functions, common components and hooks and so on.
import React, { ComponentType, ElementType, ReactNode } from "react";
import { RouteProps } from "react-router-dom";
import { Notice } from "@honeybadger-io/js/dist/server/types/core/types";
import { History } from "history";
import { StoreApi, UseBoundStore } from "zustand";
import { UseQueryOptions, UseQueryResult, UseMutationOptions, UseMutationResult, QueryKey, MutationFunction } from "@tanstack/react-query";
import { KeyPrefix, Namespace } from "i18next";
import { DefaultNamespace } from "react-i18next/TransWithoutContext";
import { qsOptionsType, QueryParamsType } from "./utils";
import { QueryFilters } from "react-query/types/core/utils";
import BrowserSupport from "@bigbinary/neeto-molecules/types/BrowserSupport";
type HoneybadgerErrorBoundaryPropsType = {
ErrorComponent?: ReactNode | ComponentType | ElementType;
filterErrors?: (error: Notice) => boolean;
children?: ReactNode;
};
type BrowserSupportPropsType = React.ComponentProps;
/**
*
* The HoneybadgerErrorBoundary is an ErrorBoundary which reports frontend errors
*
* to HoneyBadger.
*
* This component will wrap its children with the error boundary provided by
*
* @honeybadger-io/react package. In this component, we will create a honeybadger
*
* configuration object using the API key, environment and project version which we
*
* obtained from the globalProps and pass the created object to error boundary
*
* provided by honeybadger. We are also filtering the false-positive errors using
*
* beforeNotify method.
*
* For global error handling, we need to wrap the Main component with this
*
* HoneybadgerErrorBoundary component as shown below.
*
* @example
*
* !/IgnorableError/.test(notice.message)}
* >
*
*
* @endexample
* ErrorComponent is the component which will be rendered when an error is thrown
*
* during rendering. This is an optional prop and if this prop is not provided then
*
* honeybadger will use DefaultErrorComponent for fallback UI.
*
* filterErrors is a function which takes the honeybadger notice as argument and
*
* returns a boolean. It should return true if the error is to be reported and
*
* false if not.
*
*/
export const HoneybadgerErrorBoundary: React.FC;
/**
*
* The QueryClientProvider component wraps the application with a query client provider from @tanstack/react-query, facilitating data fetching and caching capabilities across the application using a predefined queryClient.
*
* @example
*
* import QueryClientProvider from "@bigbinary/neeto-commons-frontend/react-utils/QueryClientProvider";
*
* const App = () => (
*
* { Main application components }
*
*
* );
*
* export default App;
* @endexample
*/
export const QueryClientProvider: React.FC<{
children?: ReactNode;
}>;
/**
*
* The AppContainer component serves as a wrapper around your main application components,it includes error handling with HoneybadgerErrorBoundary, browser support detection with BrowserSupport, client-side routing with BrowserRouter, query management with QueryClientProvider, and development tools with ReactQueryDevtools.
*
* @example
*
* import AppContainer from "@bigbinary/neeto-commons-frontend/react-utils/AppContainer";
*
* const App = () => (
* !/IgnorableError/.test(notice.message),
* }}
* browserSupportProps={{
* checkOnMount: true,
* unsupportedBrowserComponent: UnsupportedBrowserComponent,
* }}
* reactQueryDevtoolsProps={{
* initialIsOpen: true,
* position: "bottom-right",
* }}
* enableStickyRibbons={true}
* >
* { Main application components }
*
*
* );
*
* export default App;
* @endexample
*/
export const AppContainer: React.FC<{
children?: ReactNode;
honeybadgerErrorBoundaryProps?: HoneybadgerErrorBoundaryPropsType;
browserSupportProps?: BrowserSupportPropsType;
reactQueryDevtoolsProps?: {
[key: string]: any;
};
toastContainerProps?: {
[key: string]: any;
};
enableStickyRibbons?: boolean;
}>;
/**
*
* PrivateRoute is a route that will restrict access to it based on a specified
*
* condition and permissions.
*
* If the given condition is true and the user has the required permissions, it
*
* acts identical to a normal route. If the condition is true and the user does not
*
* have the required permissions, it will show 403 error page or it will render a
*
* supplied error page. If the condition is false, it will redirect user to a
*
* different path if redirectRoute is specified or it will render a supplied error
*
* page.
*
* If condition is not specified, it will assume the value of
*
* globalProps.authenticated by default.
*
* Here are some example use cases:
*
*/
export function PrivateRoute(props: {
condition?: boolean;
redirectRoute?: string;
errorPage?: React.ReactNode;
permissions?: string[];
} & RouteProps): JSX.Element;
type OptionsType = {
root?: Element | null;
rootMargin?: String;
threshold?: Number | Number[];
};
/**
*
* Component to set the page title.
*
* @example
*
* import PageTitle from "@bigbinary/neeto-molecules/PageTitle";
*
* const Page = () => (
*
*
* Page content
*
* );
* @endexample
*/
export const PageTitle: React.FC<{
title?: string;
}>;
/**
*
* The useIsElementVisibleInDom hook is a utility that allows you to determine
*
* whether a target element is currently visible within the viewport or intersects
*
* with an ancestor element. In simpler terms, it helps you know when a specified
*
* element is scrolled out of or scrolled into the screen's visible area.
*
* The following code snippet demonstrates the usage of useIsElementVisibleInDom
*
* to display the heading element.
*
* @example
*
* import { useIsElementVisibleInDom } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* const ref = useRef(null);
* const isHeadingWrapperVisible = useIsElementVisibleInDom(ref.current, {
* threshold: 0.5,
* });
*
* return (
*
*
*
Scroll down to next section
*
*
* {isHeadingWrapperVisible &&
Hello I'm on the screen }
*
*
* );
* @endexample
* The hook watches for changes in the visibility of the referenced div element
*
* and provides a boolean value that you can use to conditionally render the header
*
* element with the text Hello I'm on the screen.
*
*/
export function useIsElementVisibleInDom(target: Element | null, options?: OptionsType): Boolean;
/**
*
* The useDebounce hook is a utility that allows you to wrap around a frequently
*
* updating state to retrieve the previous value until the updates are halted. This
*
* is particularly useful when you want to limit the number of API calls triggered
*
* by user inputs, such as a search box. The value returned by this hook will only
*
* reflect the latest value when the hook has not been called for the specified
*
* time period delay.
*
* The following code snippet demonstrates the usage of useDebounce in a search
*
* feature.
*
* @example
*
* import { useDebounce } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* const [searchKey, setSearchKey] = useState("");
* const debouncedSearchKey = useDebounce(searchKey, 300);
*
* useEffect(() => {
* // will be triggered with the latest value of input
* // only after 300ms after the user stops typing
* }, [debouncedSearchKey]);
*
* // component
* setSearchKey(e.target.value)} />;
* @endexample
* In the case of a search box, implemented without debouncing, every keystroke
*
* typically triggers a search query to find matching results. However, this rapid
*
* succession of queries can lead to several issues like performance overhead, poor
*
* user experience and inefficient resource usage.
*
* The useDebounce hook elegantly solves these problems by delaying the execution
*
* of the search function until the user pauses typing for a specified duration.
*
* This ensures that only one API request is made for the entire search operation,
*
* which solves all the issues mentioned earlier.
*
* You can learn more about the concept of debouncing
*
* here.
*
*/
export function useDebounce(value: T, delay?: number): T;
/**
*
* The useFuncDebounce hook is a utility that extends the benefits of debouncing
*
* to functions.
*
* When the debounced function is called, it sets a timer to execute the original
*
* function after a specified delay. If the debounced function is called again
*
* before the timer expires, the previous timer is cleared. This effectively delays
*
* the execution of the original function until the debounced function hasn't been
*
* called for the specified delay period.
*
* The hook also provides a cancel method to manually cancel the execution of the
*
* debounced function before it triggers.
*
* The following code snippet demonstrates the usage of useFuncDebounce in the
*
* delaying the invocation of the fetch method until the user pauses typing for a
*
* specific period.
*
* @example
*
* import { useFuncDebounce } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* const searchForProducts = useFuncDebounce(async key => {
* // this function will be triggered once after user stops typing for 300ms
* const products = await productsApi.fetch(key);
* // do something with the products
* }, 300);
*
* // component
* searchForProducts(e.target.value)} />;
* searchForProducts.cancel()}>Cancel search ;
* @endexample
*/
export function useFuncDebounce(func: F, delay?: number): F & {
cancel: () => void;
};
/**
*
* The useLocalStorage hook is a utility for synchronizing and persisting state
*
* in the local storage of a web browser. It allows you to maintain data across
*
* page refreshes or even when the user navigates away from the page. This is
*
* useful for storing user preferences, settings, or any other data that should
*
* persist between sessions.
*
* To remove the value from local storage we can call the setter method with null
*
* or undefined.
*
* Note: Use this hook only if you need the component to re-render while
*
* updating the local storage value. If all you need is plain read and write
*
* operations on the localStorage, prefer the vanilla localStorage API functions.
*
* This hook will return an array with exactly two values just like useState
*
* hook.
*
* The following code snippet illustrates the usage of useLocalStorage in
*
* implementing a theme-switching feature.
*
* @example
*
* import { useLocalStorage } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* // here "theme" is the storage key and "light" is the initial value
* const [theme, setTheme] = useLocalStorage("theme", "light");
*
* return (
* setTheme(option.value)}
* />
* );
* @endexample
* The initial value is used when the key is not found in local storage, ensuring
*
* that a default theme is applied when the user first visits the site. Subsequent
*
* changes to the theme are automatically synchronized with local storage.
*
* The following code snippet illustrates how we can pass a function to the
*
* setValue method of useLocalStorage
*
* @example
*
* import { useLocalStorage } from "@bigbinary/neeto-commons-frontend/react-utils";
* import { append } from "ramda";
*
* // here "hiddenColumns" is the storage key and "[]" is the initial value
* const [hiddenColumns, setHiddenColumns] = useLocalStorage("hiddenColumns", []);
*
* return (
* setHiddenColumns(append(option.value))}
* />
* );
* @endexample
*/
export function useLocalStorage(key: string, initialValue?: T): [T, (value: T) => void];
/**
*
* The useOnClickOutside hook is a useful utility for detecting clicks that occur
*
* outside of a specified element. It provides an elegant way to handle scenarios
*
* where you want to close or perform specific actions when a user clicks outside
*
* of a particular component, such as a modal dialog.
*
* The following code snippet demonstrates the usage of useOnClickOutside to
*
* detect clicks outside of a dropdown element and conditionally render the
*
* options.
*
* @example
*
* import { useOnClickOutside } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* const Dropdown = () => {
* const dropdownRef = useRef(null);
* const [isDropdownOpen, setIsDropdownOpen] = useState(false);
*
* // Use the useOnClickOutside hook to close the dropdown when clicking outside of it
* useOnClickOutside(dropdownRef, () => setIsDropdownOpen(false));
*
* return (
*
*
setIsDropdownOpen(!isDropdownOpen)}>
* Toggle Dropdown
*
* {isDropdownOpen && (
*
* Option 1
* Option 2
* Option 3
*
* )}
*
* );
* };
* @endexample
*/
export function useOnClickOutside(ref: React.MutableRefObject, handler: (event: MouseEvent | TouchEvent) => any, options: {
enabled: boolean;
});
/**
*
* The usePrevious hook is a convenient utility to track the previous value of
*
* its argument before its most recent update. When it is called for the first
*
* time, it returns the initial value passed as an argument, and subsequently, it
*
* returns the previous value every time the component re-renders.
*
* The hook can be useful in monitoring modifications to form fields, such as
*
* detecting when fields become dirty, and taking action based on these
*
* alterations.
*
* The following code snippet illustrates the usage of usePrevious in tracking
*
* which fields were modified in a form. By comparing the current and previous
*
* values of form fields, you can easily identify and handle changes without the
*
* need for complex state management.
*
* @example
*
* import { usePrevious } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* // Initialize state for a form field (e.g., an input field)
* const [name, setName] = useState("");
*
* // Use the usePrevious hook to obtain the previous value of name
* const previousName = usePrevious(name);
*
* // Use useEffect to detect changes in the 'name' field
* useEffect(() => {
* // Check if the 'name' field has changed
* if (name !== previousName) {
* // The 'name' field has been modified
* // You can perform actions here, such as marking it as 'dirty'
* // or updating other parts of your application
* }
* }, [name]);
* @endexample
* In the example, the useEffect block listens for changes in the name field.
*
* When the name field is updated, it compares the current value to the previous
*
* value. If they differ, it signifies that the field has been modified, enabling
*
* you to take appropriate actions.
*
*/
export function usePrevious(value: T): T;
/**
*
* The useUpdateEffect hook is a variation of the standard useEffect hook in
*
* React. The key difference is that useUpdateEffect does not execute the
*
* provided callback during the initial component mount. Instead, it only triggers
*
* the callback when the specified dependencies change. This behavior can be
*
* advantageous when you want to perform actions or side effects in response to
*
* changes in specific variables after the initial rendering of your component.
*
* The following code snippet shows the usage of useUpdateEffect in displaying
*
* category when its value is modified.
*
* @example
*
* import { useUpdateEffect } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* // Initialize state variables
* const [category, setCategory] = useState("default");
*
* // Use useUpdateEffect to update content based on category changes
* useUpdateEffect(() => {
* // This callback will run when 'category' changes, but not during the initial mount
* console.log(`Category has been modified to ${category}`);
* }, [category]);
* @endexample
* The useUpdateEffect hook allows you to specify a callback function that will
*
* only execute when certain dependencies, in this case, the category state,
*
* change after the initial mount.
*
*/
export function useUpdateEffect(effect: () => void, deps: any[]): void;
/**
*
* The useDisplayErrorPage hook is a utility that allows you to conditionally
*
* render an error page in your application based on the status codes of API
*
* responses.
*
* The following code snippet demonstrates the usage of useDisplayErrorPage in
*
* conditionally rendering an error page.
*
* @example
*
* import { useDisplayErrorPage } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* const App = () => {
* const isError = useDisplayErrorPage();
*
* if (isError) {
* return ;
* }
*
* return ;
* };
* @endexample
*/
export function useDisplayErrorPage(): boolean;
/**
*
* A Zustand store containing the status code of the latest API failed with 403 or
*
* 404 status. It stores the following values:
*
* This store is automatically managed by neeto-commons-frontend using its axios
*
* interceptors. You can also use this store if you need to display an error page
*
* from your frontend logic.
*
*/
export const useErrorDisplayStore: UseBoundStore>;
type TimerType = {
lastUpdated: number;
interval: number;
};
/**
*
* The useTimer hook is a utility that enables automatic re-renders of a
*
* component at specified time intervals. This functionality is particularly useful
*
* for updating rendered content, such as elapsed time, without requiring manual
*
* refreshes.
*
* All invocations of useTimer hooks are attached to a single setInterval call
*
* which ticks every 1 second. So using that hook in multiple components won't
*
* cause any performance drop.
*
* Note that the maximum precision for useTimer hook is one second. In other
*
* words, there is a possibility that your component will take at most one more
*
* second than the scheduled time interval to re-render.
*
* The following demonstrates the usage of useTimer hook in displaying
*
* time-sensitive information.
*
* @example
*
* import { useTimer } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* const Post = () => {
* // Use the useTimer hook with a custom interval
* useTimer(30);
*
* // Calculate the elapsed time since the post creation
* const currentTime = new Date();
* const elapsedTimeInSeconds = Math.floor((currentTime - createdAt) / 1000);
*
* return Elapsed Time in seconds: {elapsedTimeInSeconds}
;
* };
* @endexample
*/
export function useTimer(interval: number): TimerType;
type ZustandConfigType = (set: (data: any) => void, get: () => any, api: any) => any;
/**
*
* withImmutableActions is a Zustand middleware function that prevents the
*
* actions from getting overwritten.
*
* @example
*
* const useStore = create(
* withImmutableActions(set => ({
* value: 10,
* // other properties ...
* setValue: ({ value }) => set({ value }),
* setGlobalState: set,
* // other actions ...
* }))
* );
* @endexample
* In the example above, any attempts like the following will trigger an error
*
* because actions should never be overwritten:
*
* @example
*
* setGlobalState({ value: 0, setValue: () => {} });
* @endexample
* However, actions can be assigned their own values. This enables you to use
*
* curried Ramda functions in conjunction with Zustand actions. For instance, the
*
* following usage will not result in an error:
*
* @example
*
* setGlobalState(state => ({ value: 0, setValue: state.setValue }));
* @endexample
* The second parameter to overwrite the entire state will be ignored. Both of the
*
* following lines of code are functionally equivalent:
*
* @example
*
* setGlobalState(state => ({ value: 0 }), true);
* setGlobalState(state => ({ value: 0 }));
* @endexample
* It should be noted that the withImmutableActions middleware intercepts and
*
* wraps the set method of the Zustand store to enforce immutability rules.
*
* Therefore, usages of useStore.setState() won't be handled by the middleware
*
* since they directly update the state in Zustand without going through the set
*
* method of the store.
*
* @example
*
* // This won't be handled by the middleware.
* useStore.setState({ value: 0, setValue: () => {} });
* @endexample
*/
export function withImmutableActions(config: ZustandConfigType): ZustandConfigType;
export declare type ZustandStoreHook = UseBoundStore> & {
pick: (path?: string | string[]) => any;
};
/**
*
* The useFieldSubmit hook simplifies the task of capturing the Enter
*
* (Return) key press event within an input field and executing a callback
*
* function when the Enter key is pressed. This is a common requirement in forms
*
* where pressing Enter should trigger specific actions, such as submitting a
*
* form or processing user input. Importantly, this hook ensures that pressing
*
* Shift + Enter does not trigger the callback, allowing for the creation of
*
* multi-line text inputs.
*
* The following code snippet illustrates the usage of useFieldSubmit in building
*
* an interactive textarea input field.
*
* @example
*
* import { useFieldSubmit } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* // Create a ref for the input field using useFieldSubmit
* const inputRef = useFieldSubmit(() => {
* const inputValue = inputRef.current.value;
* console.log(inputValue);
* });
*
* return ;
* @endexample
* When the Enter key is pressed, the onSubmit callback function is executed,
*
* allowing you to access the input value and perform the desired action.
*
*/
export function useFieldSubmit(onSubmit: () => any): {
current: HTMLInputElement & HTMLTextAreaElement;
};
/**
*
* withTitle is an HOC which sets the browser title with the given value when the
*
* wrapped component is rendered. If title is not explicitly provided, it will
*
* render globalProps.appName as the page title.
*
* @example
*
* // assume this code in neetoStore
* const ProductsPage = props => <>Your page content here>;
*
* // will set the browser title to `All products | neetoStore` when this page is rendered.
* export default withTitle(ProductsPage, "All products");
* @endexample
*/
export function withTitle(Component: React.ComponentType, title?: string | null): (props: T) => JSX.Element;
/**
*
* registerBrowserNotifications is a browser push notifications utility function
*
* which asks the user permissions to send notifications and then register the
*
* browser with the notification service.
*
* To enable the notification service to dispatch web push messages to browsers, it
*
* relies on VAPID keys. These
*
* keys should be securely stored as environment variables and made accessible
*
* through process.env. If keys are not accessible from process.env, pass in
*
* the vapidPublicKey using global props from application helper.
*
* You can find the VAPID keys (PUBLIC and PRIVATE) for neeto products at
*
* neetoNotifications Dashboard.
*
* After the user logs in, we need to ask the user permissions to send
*
* notifications and then register the browser with the notification service. The
*
* registerBrowserNotifications utility function facilitates this process. It can
*
* be called on any event after login based on the application's logic and
*
* requirement.
*
* Here as an example, we are calling registerBrowserNotifications method inside
*
* the Login component after the Authentication API request. This helps to
*
* associate the browser for that particular user.
*
* @example
*
* import { registerBrowserNotifications } from "neetocommons/react-utils";
*
* const handleLogin = async () => {
* try {
* // Authentication API request
* await registerBrowserNotifications();
* history.push(DASHBOARD_URL);
* } catch {}
* };
* @endexample
* The above mentioned feature is currently supported for chromium-based browsers.
*
* Safari (iOS) is currently WIP.
*
* Any other browser which is not mentioned in above will be considered as
*
* unsupported.
*
*/
export async function registerBrowserNotifications(): Promise;
/**
*
* destroyBrowserSubscription is a browser push notifications utility function
*
* which destroys the browser subscriptions from the user's devices. This helps in
*
* unsubscribing from browser push notifications.
*
* When the browser subscriptions expires for the user's devices or when the user
*
* decides to logout, the generated browser subscription for the user should be
*
* destroyed. The destroyBrowserSubscription utility function enables us to do
*
* the same. It can be called on any event before logout based on the application's
*
* logic and requirement.
*
* Here is an example: In the Logout component, we are calling
*
* destroyBrowserSubscription method before the Logout API request. This
*
* helps to destroy the browser subscription from the user's devices.
*
* @example
*
* import { destroyBrowserSubscription } from "neetocommons/react-utils";
*
* const handleLogout = async () => {
* try {
* await destroyBrowserSubscription(); //Before logout request
* // Logout API request
* window.location.href = LOGIN_PATH;
* } catch {}
* };
* @endexample
* The above mentioned feature is currently supported for chromium-based browsers.
*
* Safari (iOS) is currently WIP.
*
* Any other browser which is not mentioned in above will be considered as
*
* unsupported.
*
*/
export async function destroyBrowserSubscription(): Promise;
type handleMetaClickOptions = {
isHistoryReplace?: boolean;
};
/**
*
* The handleMetaClick function can be used to handle onClick actions that
*
* redirects to a URL. It opens up the URL in a new tab if ctrl/cmd + click event
*
* is received. Otherwise, simply redirects to the provided URL in the same tab.
*
* URL can be passed as string or a history location object can be passed instead.
*
* Let's say you have a navigation menu with several links, and you want to allow
*
* users to open these links in a new tab if they hold down the Ctrl (or Cmd on
*
* macOS) key while clicking, and if they click without holding down the Ctrl key,
*
* the link should open in the same tab. You can use handleMetaClick to achieve
*
* this behavior.
*
* @example
*
* const NavigationMenu = () => {
* const history = useHistory();
*
* const handleLinkClick = (url, event) => {
* // Opens url in a new tab if metaKey/CtrlKey is pressed.
* // Otherwise simply redirects to url.
* handleMetaClick(history, url, event);
* };
*
* return (
*
* );
* };
* @endexample
*/
export function handleMetaClick(history: History, params: string | object, event: React.MouseEvent, options?: handleMetaClickOptions): void;
/**
*
* The handleMetaClick function can be used to handle onClick actions that
*
* redirects to a URL. It opens up the URL in a new tab if ctrl/cmd + click event
*
* is received. Otherwise, simply redirects to the provided URL in the same tab.
*
* URL can be passed as string or a history location object can be passed instead.
*
* Let's say you have a navigation menu with several links, and you want to allow
*
* users to open these links in a new tab if they hold down the Ctrl (or Cmd on
*
* macOS) key while clicking, and if they click without holding down the Ctrl key,
*
* the link should open in the same tab. You can use handleMetaClick to achieve
*
* this behavior.
*
* @example
*
* const NavigationMenu = () => {
* const history = useHistory();
*
* const handleLinkClick = (url, event) => {
* // Opens url in a new tab if metaKey/CtrlKey is pressed.
* // Otherwise simply redirects to url.
* handleMetaClick(history, url, event);
* };
*
* return (
*
* );
* };
* @endexample
*/
export function handleMetaClick(history: History, params: string | object, event: React.MouseEvent): (options?: handleMetaClickOptions) => void;
/**
*
* The handleMetaClick function can be used to handle onClick actions that
*
* redirects to a URL. It opens up the URL in a new tab if ctrl/cmd + click event
*
* is received. Otherwise, simply redirects to the provided URL in the same tab.
*
* URL can be passed as string or a history location object can be passed instead.
*
* Let's say you have a navigation menu with several links, and you want to allow
*
* users to open these links in a new tab if they hold down the Ctrl (or Cmd on
*
* macOS) key while clicking, and if they click without holding down the Ctrl key,
*
* the link should open in the same tab. You can use handleMetaClick to achieve
*
* this behavior.
*
* @example
*
* const NavigationMenu = () => {
* const history = useHistory();
*
* const handleLinkClick = (url, event) => {
* // Opens url in a new tab if metaKey/CtrlKey is pressed.
* // Otherwise simply redirects to url.
* handleMetaClick(history, url, event);
* };
*
* return (
*
* );
* };
* @endexample
*/
export function handleMetaClick(history: History, params: string | object): (event: React.MouseEvent, options?: handleMetaClickOptions) => void;
/**
*
* The handleMetaClick function can be used to handle onClick actions that
*
* redirects to a URL. It opens up the URL in a new tab if ctrl/cmd + click event
*
* is received. Otherwise, simply redirects to the provided URL in the same tab.
*
* URL can be passed as string or a history location object can be passed instead.
*
* Let's say you have a navigation menu with several links, and you want to allow
*
* users to open these links in a new tab if they hold down the Ctrl (or Cmd on
*
* macOS) key while clicking, and if they click without holding down the Ctrl key,
*
* the link should open in the same tab. You can use handleMetaClick to achieve
*
* this behavior.
*
* @example
*
* const NavigationMenu = () => {
* const history = useHistory();
*
* const handleLinkClick = (url, event) => {
* // Opens url in a new tab if metaKey/CtrlKey is pressed.
* // Otherwise simply redirects to url.
* handleMetaClick(history, url, event);
* };
*
* return (
*
* );
* };
* @endexample
*/
export function handleMetaClick(history: History): (params: string | object, event: React.MouseEvent, options?: handleMetaClickOptions) => void;
/**
*
* The isMetaKeyPressed function can be used to check whether an onClick event
*
* has metaKey or ctrlKey pressed.
*
* @example
*
* isMetaKeyPressed(event);
* @endexample
*/
export function isMetaKeyPressed(event: React.MouseEvent): boolean;
/**
*
* The useStateWithDependency hook is a utility that returns a state variable and
*
* a setter function similar to the useState hook. However, it provides
*
* additional functionality: the state variable automatically updates its value to
*
* the specified defaultValue whenever defaultValue changes. Optionally, you
*
* can also specify a dependencies array to listen for changes in other variables,
*
* which will trigger the update of the state variable.
*
* This hook will return an array with exactly two values just like useState
*
* hook.
*
* If dependencies is passed, the hook returns a state variable whose value will
*
* be updated to defaultValue in a useEffect that depends on the dependencies
*
* array. Otherwise, the useEffect will depend only on the defaultValue.
*
* @example
*
* import { useStateWithDependency } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* // reset state to `defaultValue` whenever `defaultValue` changes.
* const [value, setValue] = useStateWithDependency(defaultValue);
*
* // reset state to `defaultValue` only when `value1` or `value2` changes.
* const [value, setValue] = useStateWithDependency(defaultValue, [
* value1,
* value2,
* ]);
*
* // In a case, where the value needs to be reset when defaultValue
* // changes too, defaultValue can be passed with the dependencies,
* // as shown below.
* const [value, setValue] = useStateWithDependency(defaultValue, [
* value1,
* value2,
* defaultValue,
* ]);
* @endexample
* The following code snippet demonstrates the usage of useStateWithDependency in
*
* ensuring that inputValue stays in sync with the value prop in the Rename
*
* component.
*
* @example
*
* const Rename = ({ value }) => {
* // Use the useStateWithDependency hook to manage the input value
* const [inputValue, setInputValue] = useStateWithDependency(value);
*
* // Define a handler to update the input value
* const handleInputChange = event => {
* setInputValue(event.target.value);
* };
*
* return (
*
*
* { Rest of the component }
*
* );
* };
* @endexample
* As the user interacts with the input field, the inputValue state is updated to
*
* reflect the user's input. In a scenario where the value prop changes from
*
* outside the Rename component, it triggers a re-render of the Rename
*
* component. When this re-render occurs, the inputValue state is automatically
*
* updated to match the new value prop.
*
*/
export function useStateWithDependency(defaultValue: T, dependencies?: any[]): T;
/**
*
* The useRegisterNavigationCheckpoint hook is a utility for storing navigation
*
* checkpoints in a Zustand store. A navigation checkpoint consists of a key that
*
* identifies the checkpoint and a corresponding path to be stored in the Zustand
*
* store under that key. These registered checkpoints can be later retrieved using
*
* the useNavigationCheckpoints hook.
*
* In web applications, scenarios frequently arise where it's essential to maintain
*
* user preferences and specific application states as users navigate back and
*
* forth between different routes.
*
* A simple button, that hard codes the route as , won't
*
* suffice in such scenarios to navigate between pages since it won't retain any
*
* filter, search, or pagination information.
*
* This is why we have checkpoints. When a page component is mounted, a checkpoint
*
* is registered to capture the current state of the page, including the URL with
*
* applied filters, search parameters, and pagination information. If the URL
*
* changes due to user interactions, the checkpoint is automatically updated.
*
* When a user presses the back button, the value stored in the checkpoint is used
*
* as the URL. This ensures that users return to the listing page with all their
*
* previous context intact, providing a seamless and user-friendly experience.
*
* Following code snippet illustrates the usage of
*
* useRegisterNavigationCheckpoint in retaining user preference and context.
*
* @example
*
* // Import the useRegisterNavigationCheckpoint hook
* import { useRegisterNavigationCheckpoint } from "@bigbinary/neeto-commons-frontend/react-utils";
* // ...
*
* // Register a navigation checkpoint with a key and path
* useRegisterNavigationCheckpoint(
* NAVIGATION_CHECKPOINT_KEYS.ARTICLES,
* window.location.pathname + window.location.search
* );
* @endexample
* When users visit the articles page, apply specific filters, and navigate away,
*
* the navigation checkpoint captures the current URL path along with the query
*
* parameters associated with the applied filters. Later, when users return to the
*
* page, the useNavigationCheckpoints hook can retrieve the saved checkpoint
*
* using the NAVIGATION_CHECKPOINT_KEYS.ARTICLES key. This retrieved data
*
* includes the applied filters, which can then be displayed on the articles page.
*
* It is worth noting that, at BigBinary, we define the object
*
* NAVIGATION_CHECKPOINT_KEYS to store the check point keys like so:
*
* @example
*
* export const NAVIGATION_CHECKPOINT_KEYS = {
* HOME: "home",
* ARTICLES: "articles",
* };
* @endexample
*/
export function useRegisterNavigationCheckpoint(key: string, path?: string, persistInLocalStorage?: boolean): void;
/**
*
* The useNavigationCheckpoints hook is a convenient tool for fetching designated
*
* navigation checkpoints stored in a Zustand store.
*
* In web applications, users frequently move between various pages or routes, and
*
* useRegisterNavigationCheckpoint empowers us to store particular paths or
*
* states as checkpoints. By employing the useNavigationCheckpoints hook, you can
*
* access these checkpoints by specifying their associated keys.
*
* We recommend reviewing the documentation for the
*
* useRegisterNavigationCheckpoint hook to
*
* gain a deeper understanding of the checkpoint concept.
*
* The following code snippet demonstrates the usage of useNavigationCheckpoints
*
* in retrieving navigation links that reflect the application's state and history.
*
* @example
*
* // Import the useNavigationCheckpoints hook
* import { useNavigationCheckpoints } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* const navigationCheckpoints = useNavigationCheckpoints("home");
*
* return (
* <>
* Home
* >
* );
* @endexample
* The above code fetches navigation checkpoint data for the home key using the
*
* useNavigationCheckpoints hook and then renders a link labeled "Home" that,
*
* when clicked, will navigate the user to the URL associated with the home
*
* navigation checkpoint.
*
*/
export function useNavigationCheckpoints(...keys: string[]): {
[key: string]: string;
};
/**
*
* The useRemoveNavigationCheckpoint hook is a utility for removing a navigation
*
* checkpoint from the navigation checkpoints stored in the Zustand store.
*
* In web applications, users frequently move between various pages or routes, and
*
* useRegisterNavigationCheckpoint empowers us to store particular paths or
*
* states as checkpoints. By employing the useNavigationCheckpoints hook, you can
*
* access these checkpoints by specifying their associated keys. There might arise
*
* a case where you want to remove a particular checkpoint from the Zustand store,
*
* useRemoveNavigationCheckpoint can be used for this.
*
* We recommend reviewing the documentation for the
*
* useRegisterNavigationCheckpoint and useNavigationCheckpoints hooks for
*
* gaining a deeper understanding of the checkpoint concept.
*
* The following code snippet demonstrates the usage of
*
* useRemoveNavigationCheckpoint in removing a navigation checkpoint from the
*
* Zustand store.
*
* @example
*
* // Import the useRemoveNavigationCheckpoint hook
* import { useRemoveNavigationCheckpoint } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* const removeCheckpoint = useRemoveNavigationCheckpoint();
*
* removeCheckpoint("home");
* @endexample
* The above code removes the navigation checkpoint corresponding to the home key
*
* using the useRemoveNavigationCheckpoint hook.
*
*/
export function useRemoveNavigationCheckpoint(): (key: string) => void;
/**
*
* The useRestoreScrollPosition hook facilitates the restoration and synchronization of scroll positions for components using Zustand store.
*
* Maintaining scroll positions as users navigate between different routes or interact with dynamic content is crucial for preserving user context and enhancing usability. useRestoreScrollPosition addresses this by allowing components to save and restore scroll positions based on specified keys.
*
* Here's an example demonstrating how to use useRestoreScrollPosition to manage scroll positions within a React component:
*
* @example
*
* import { useRef } from "react";
* import useRestoreScrollPosition, { useRemoveScrollPosition } from "@bigbinary/neeto-commons-frontend/react-utils/useRestoreScrollPosition";
*
* const ScrollableComponent = ({ scrollKey }) => {
* const ref = useRef(null);
*
* // Hook to manage scroll position restoration
* useRestoreScrollPosition(scrollKey, ref);
*
* // Example of how to remove a saved scroll position
* const removeScrollPosition = useRemoveScrollPosition();
* const handleClearScrollPosition = () => {
* removeScrollPosition(scrollKey);
* };
*
* return (
*
* { Scrollable content }
* Clear Scroll Position
*
* );
* }
*
* export default ScrollableComponent;
* @endexample
*/
export function useRestoreScrollPosition(key: string, ref: React.RefObject): void;
/**
*
* A custom hook that provides access to set scroll positions.
*
* @example
*
* import { useSetScrollPosition } from "@bigbinary/neeto-commons-frontend/react-utils/useRestoreScrollPosition
*
* const setScrollPosition = useSetScrollPosition();
* setScrollPosition("myKey", 100);
* @endexample
*/
export function useSetScrollPosition(): (key: string, value: number) => void;
/**
*
* A custom hook that retrieves the scroll position for a given key.
*
* @example
*
* import { useGetScrollPosition } from "@bigbinary/neeto-commons-frontend/react-utils/useRestoreScrollPosition
*
* const getScrollPosition = useGetScrollPosition();
* const position = getScrollPosition("myKey");
* @endexample
*/
export function useGetScrollPosition(): (key: string) => number | undefined;
/**
*
* A custom hook that removes a scroll position entry for a specified key.
*
* @example
*
* import { useRemoveScrollPosition } from "@bigbinary/neeto-commons-frontend/react-utils/useRestoreScrollPosition
*
* const removeScrollPosition = useRemoveScrollPosition();
* removeScrollPosition("myKey");
* @endexample
*/
export function useRemoveScrollPosition(): (key: string) => void;
/**
*
* The useFuncDebounce hook is a utility that extends the benefits of debouncing
*
* to functions.
*
* When the debounced function is called, it sets a timer to execute the original
*
* function after a specified delay. If the debounced function is called again
*
* before the timer expires, the previous timer is cleared. This effectively delays
*
* the execution of the original function until the debounced function hasn't been
*
* called for the specified delay period.
*
* The hook also provides a cancel method to manually cancel the execution of the
*
* debounced function before it triggers.
*
* The following code snippet demonstrates the usage of useFuncDebounce in the
*
* delaying the invocation of the fetch method until the user pauses typing for a
*
* specific period.
*
* @example
*
* import { useFuncDebounce } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* const searchForProducts = useFuncDebounce(async key => {
* // this function will be triggered once after user stops typing for 300ms
* const products = await productsApi.fetch(key);
* // do something with the products
* }, 300);
*
* // component
* searchForProducts(e.target.value)} />;
* searchForProducts.cancel()}>Cancel search ;
* @endexample
*/
export const useFuncDebounce: {
(func: F, delay?: number): F;
cancel: () => void;
};
/**
*
* usePersistedQuery is an wrapper function around
*
* useQuery hook
*
* from react-query. In addition to all the functionalities offered useQuery,
*
* this hook also stores the received response in localStorage. For subsequent
*
* page loads, the hook will be serving data from localStorage until the API
*
* response is received.
*
* usePersistedQuery will use the staleTime config from options parameter (if
*
* present) to determine the expiry of the localStorage cache. The hook will not
*
* return data from localStorage if it is expired. Instead it will wait for the
*
* API response to become available.
*
* You can use isFreshLoading property from the return value of the hook to know
*
* whether the hook is waiting for the API. data won't be available (will be
*
* undefined) until isFreshLoading turns to false.
*
* It also provides a method getCache to get the cached data from localStorage
*
* by providing the queryKey.
*
* The following code snippet demonstrates the usage of usePersistedQuery in
*
* caching API responses and serving cached data while waiting for fresh data from
*
* the APIs.
*
* @example
*
* import { usePersistedQuery } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* const useFetchUsers = options =>
* usePersistedQuery(QUERY_KEYS.FETCH_USERS, usersApi.fetch, options);
*
* // Now we can use useFetchUsers as:
* const { data, isLoading, isFreshLoading } = useFetchUsers();
*
* // To get the cached data from localStorage
* const cachedUsersData = usePersistedQuery.getCache(QUERY_KEYS.FETCH_USERS);
* @endexample
*/
export const usePersistedQuery: {
(queryKey: string | unknown[], fetch: () => Promise, options?: UseQueryOptions): UseQueryResult & {
isFreshLoading: boolean;
};
getCache: (queryKey: string | unknown[]) => T | null;
};
/**
*
* The useFetchNeetoApps hook calls
*
* neeto_apps_controller
*
* and fetches the response. neeto_apps_controller will return two information:
*
* This hook uses usePersistedQuery under the hood. So
*
* data will be immediately available except for the first page load. Additionally
*
* if the data is available in localStorage, it will be served from there until an
*
* explicit call to refetch is made.
*
* The following code snippet shows the usage of useFetchNeetoApps in fetching
*
* neetoApps and ownership status.
*
* @example
*
* import { useFetchNeetoApps } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* const { neetoApps, isOwner } = useFetchNeetoApps();
* @endexample
*/
export function useFetchNeetoApps(options?: UseQueryOptions): ReturnType>;
/**
*
* withT is an HOC which provides the t function from react-i18next to the
*
* wrapped component as a prop. withT HOC should only be used in dumb components
*
* i.e, components which have no logic other than rendering something and the
*
* useTranslation hook.
*
* @example
*
* // Example usage of withT:
* const ComponentWithTranslation = withT(({ t }) => {t("some.key")}
);
*
* // Example usage of withT with forwarded ref:
* const ComponentWithRef = withT(
* React.forwardRef(({ t }, ref) => {t("some.key")}
),
* {
* withRef: true,
* }
* );
*
* // Example usage of withT with keyPrefix:
* const ComponentWithTranslation = withT(
* ({ t }) => (
* // t function will be called with the key "very.nested.key.some.key"
* {t("some.key")}
* ),
* {
* keyPrefix: "very.nested.key",
* }
* );
* @endexample
*/
export const withT: = undefined>(Component: React.ComponentType, options?: {
withRef?: boolean;
keyPrefix?: TKPrefix;
}, ns?: N) => React.ComponentType;
type QueryKeyOrFilters = QueryKey | QueryFilters;
/**
*
* useMutationWithInvalidation is a wrapper function around the
*
* useMutation
*
* hook from react-query. It returns all the props from the useMutation hook and
*
* additionally, invalidates a list of query keys on mutation success.
*
* The following code snippet illustrates the usage of
*
* useMutationWithInvalidation across various scenarios.
*
* @example
*
* // useQuestionsApi
* //Invalidate any query with query key starting with "QUESTIONS"
* const useUpdateQuestions = () =>
* useMutationWithInvalidation(questionsApi.update, {
* keysToInvalidate: ["QUESTIONS"],
* onSuccess: data => queryClient.setQueryData(["RESPONSES"], data),
* });
*
* // The above is the same as writing
* const useUpdateQuestions = options =>
* useMutation(questionsApi.update, {
* ...options,
* onSuccess: (data, ...args) => {
* queryClient.invalidateQueries({
* queryKey: ["QUESTIONS"]
* });
* queryClient.setQueryData(["RESPONSES"], data);
* options?.onSuccess(data, ...args);
* },
* });
* @endexample
* @example
*
* //Invalidate any query with query key starting with ["FORM", "QUESTIONS"]
* const useUpdateQuestions = options =>
* useMutationWithInvalidation(questionsApi.update, {
* keysToInvalidate: [["FORM", "QUESTIONS"]],
* ...options,
* });
*
* // The above is the same as writing
* const useUpdateQuestions = options =>
* useMutation(questionsApi.update, {
* ...options,
* onSuccess: (...args) => {
* queryClient.invalidateQueries({
* queryKey: ["FORM", "QUESTIONS"],
* });
* options?.onSuccess(...args);
* },
* });
* @endexample
* @example
*
* //Invalidate any query with query key starting with "FORM" or starting with "QUESTIONS"
* const useUpdateQuestions = options =>
* useMutationWithInvalidation(questionsApi.update, {
* keysToInvalidate: ["FORM", "QUESTIONS"],
* ...options,
* });
*
* // The above is the same as writing
* const useUpdateQuestions = options =>
* useMutation(questionsApi.update, {
* ...options,
* onSuccess: () => {
* queryClient.invalidateQueries({
* queryKey: ["FORM"],
* });
* queryClient.invalidateQueries({
* queryKey: ["QUESTIONS"],
* });
* options?.onSuccess(...args);
* },
* });
* @endexample
* @example
*
* //Invalidate any query with query key starting with ["QUESTION", id]
* const useUpdateQuestions = options =>
* useMutationWithInvalidation(questionsApi.update, {
* keysToInvalidate: [(_, { id }) => ["QUESTION", id]],
* ...options,
* });
*
* // The above is the same as writing
* const useUpdateQuestions = options =>
* useMutation(questionsApi.update, {
* ...options,
* onSuccess: (...args) => {
* const [_, { id }] = args;
* queryClient.invalidateQueries({
* queryKey: ["QUESTION", id],
* });
* options?.onSuccess(...args);
* },
* });
*
* // Update question component
* const { mutate: updateQuestions, isPending } = useUpdateQuestions({
* onError: e => logger.error(e),
* });
* @endexample
*/
export function useMutationWithInvalidation(mutationFn: MutationFunction, options: {
keysToInvalidate: Array QueryKeyOrFilters)>;
} & Omit, 'mutationFn'>): UseMutationResult;
/**
*
* The useQueryParams hook can be used to retrieve the query parameters whenever
*
* the location changes. A change in location will trigger rerender when using this
*
* hook.
*
* This hook will return an object containing all the query parameters in camel
*
* case. It will return an empty object if no query parameters are present.
*
* You can set toCamelCase to false if you don't want to apply any transformation to the keys.
*
* More details on working:
*
* https://www.loom.com/share/4f369cff56e845428d5ef7451759469c
*
* @example
*
* const queryParams = useQueryParams();
* // output: { sort_by: "description", order_by: "desc" }
* const { sortBy, orderBy } = useQueryParams();
*
* const { sort_by, order_by } = useQueryParams({ toCamelCase: false });
* @endexample
*/
export function useQueryParams(options?: qsOptionsType): QueryParamsType;
type BreakpointKeys = 'mobile' | 'tablet' | 'desktop' | 'largeDesktop';
type Breakpoints = { [key in BreakpointKeys]: boolean };
/**
*
* The useBreakpoints hook is a utility for responsive styling on components
*
* based on the viewport size. It returns the current breakpoint name and a
*
* function to check if the current breakpoint matches a given breakpoint name. It
*
* also re-renders the component when the current breakpoint changes.
*
* Note: Use this hook only if you need the component to re-render when the
*
* current breakpoint change happens. If all you need is plain read and write
*
* operations on the current breakpoint, prefer the vanilla window.matchMedia or
*
* window.innerWidth API.
*
* @example
*
* (window) => {
* mobile: window.innerWidth < 768,
* tablet: window.innerWidth >= 768 && window.innerWidth < 1024,
* desktop: window.innerWidth >= 1024 && window.innerWidth < 1280,
* largeDesktop: window.innerWidth >= 1280,
* }
* @endexample
* This hook will return an object with two values currentSize and isSize.
*
* The following code snippet illustrates the usage of useLocalStorage in
*
* implementing a theme-switching feature.
*
* @example
*
* import { useBreakpoints } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* const { currentSize, isSize } = useBreakpoints();
*
* // When window.innerWidth is 1028
* console.log(currentSize); // logs `tablet`
* console.log(isSize("mobile")); // logs `false`
* console.log(isSize("tablet")); // logs `true`
* @endexample
* We can also override the default breakpoints by passing a function to the
*
* useBreakpoints hook.
*
* @example
*
* import { useBreakpoints } from "@bigbinary/neeto-commons-frontend/react-utils";
*
* // Overriding the default breakpoints
* const { currentSize, isSize } = useBreakpoints(window => ({
* largeDesktop: window.innerWidth >= 1280 && window.innerWidth < 1440,
* extraLargeDesktop: window.innerWidth >= 1440,
* }));
*
* // When window.innerWidth is 1440
* console.log(currentSize); // logs `extraLargeDesktop`
* console.log(isSize("extraLargeDesktop")); // logs `true`
* @endexample
*/
export function useBreakpoints(breakpointOverrides?: (window: Window) => Partial): {
currentSize: BreakpointKeys | keyof T;
isSize: (size: BreakpointKeys | keyof T) => boolean;
};