@alextheman/components 7.0.2 → 7.0.4

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.
@@ -142,7 +142,7 @@ interface QueryBoundaryContextValue<DataType> {
142
142
  /** The data being loaded. */
143
143
  data?: DataType | null | undefined;
144
144
  /** The error given if the request gave an error. */
145
- error: unknown;
145
+ error?: unknown;
146
146
  }
147
147
  type QueryBoundaryProviderProps<DataType> = QueryBoundaryContextValue<DataType> & {
148
148
  children: ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/QueryBoundary/QueryBoundaryProvider.tsx","../../src/QueryBoundary/QueryBoundaryData.tsx","../../src/QueryBoundary/QueryBoundaryDataMap.tsx","../../src/QueryBoundary/QueryBoundaryError.tsx","../../src/QueryBoundary/QueryBoundaryNullable.tsx","../../src/QueryBoundary/QueryBoundaryFallback.tsx","../../src/QueryBoundary/creators/createBaseQueryBoundary.tsx","../../src/QueryBoundary/creators/createItemQueryBoundary.tsx","../../src/QueryBoundary/QueryBoundaryItemWrapper.tsx","../../src/QueryBoundary/creators/createListQueryBoundary.tsx","../../src/QueryBoundary/QueryBoundaryListWrapper.tsx"],"sourcesContent":["import type { OptionalOnCondition } from \"@alextheman/utility\";\nimport type { ReactNode } from \"react\";\n\nimport type { ContextHookOptions } from \"src/root/types\";\n\nimport { DataError } from \"@alextheman/utility/v6\";\nimport { createContext, use } from \"react\";\n\nexport interface QueryBoundaryContextValue<DataType> {\n /** The current loading status (true if loading, false if not) */\n isLoading?: boolean;\n /** The data being loaded. */\n data?: DataType | null | undefined;\n /** The error given if the request gave an error. */\n error: unknown;\n}\n\nexport type QueryBoundaryProviderProps<DataType> = QueryBoundaryContextValue<DataType> & {\n children: ReactNode;\n};\n\nconst QueryBoundaryContext = createContext<QueryBoundaryContextValue<unknown> | undefined>(\n undefined,\n);\n\n/** Access the QueryBoundary context directly. */\nexport function useQueryBoundaryContext<DataType, Strict extends boolean = true>({\n strict = true as Strict,\n}: ContextHookOptions<Strict> = {}): OptionalOnCondition<\n Strict,\n QueryBoundaryContextValue<DataType>\n> {\n const context = use(QueryBoundaryContext);\n if (strict && !context) {\n throw new DataError(\n { strict, context },\n \"QUERY_BOUNDARY_PROVIDER_NOT_FOUND\",\n \"Could not find the QueryBoundaryProvider context. Please double-check that it is present.\",\n );\n }\n return context as OptionalOnCondition<Strict, QueryBoundaryContextValue<DataType>>;\n}\n\n/**\n * A provider for a context that deals with state management when fetching data from an API.\n * This may be used over QueryBoundary if you require more control over the placement of the error message and data display.\n *\n * @template DataType - The type of data being loaded.\n */\nfunction QueryBoundaryProvider<DataType>({\n children,\n ...contextProps\n}: QueryBoundaryProviderProps<DataType>) {\n return <QueryBoundaryContext value={contextProps}>{children}</QueryBoundaryContext>;\n}\n\nexport default QueryBoundaryProvider;\n","import type { ReactNode } from \"react\";\n\nimport CircularProgress from \"@mui/material/CircularProgress\";\n\nimport { useQueryBoundaryContext } from \"src/QueryBoundary/QueryBoundaryProvider\";\n\nexport interface QueryBoundaryDataProps<DataType> {\n /**\n * The elements to show after data has been loaded.\n * This is best provided as a function with a data argument that guarantees the data will not be undefined by the time you receive it here.\n */\n children: ReactNode | ((data: NonNullable<DataType>) => ReactNode);\n /** A parser for the data. */\n dataParser?: (data: unknown) => NonNullable<DataType>;\n /** The component to show when the data is being fetched. */\n loadingFallback?: ReactNode;\n}\n\n/**\n * The component responsible for showing the data provided by QueryBoundaryProvider.\n *\n * @template DataType - The type of data being loaded.\n */\nfunction QueryBoundaryData<DataType>({\n children,\n dataParser,\n loadingFallback = <CircularProgress />,\n}: QueryBoundaryDataProps<DataType>) {\n const { isLoading, data, error } = useQueryBoundaryContext<DataType>();\n\n if (error) {\n return null;\n }\n\n if (isLoading) {\n return <>{loadingFallback}</>;\n }\n\n if (data === null || data === undefined) {\n return null;\n }\n\n return (\n <>\n {typeof children === \"function\" ? children(dataParser ? dataParser(data) : data) : children}\n </>\n );\n}\n\nexport default QueryBoundaryData;\n","import type { Key, ReactNode } from \"react\";\n\nimport { DataError } from \"@alextheman/utility/v6\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport Typography from \"@mui/material/Typography\";\nimport { Fragment } from \"react\";\n\nimport { useQueryBoundaryContext } from \"src/QueryBoundary/QueryBoundaryProvider\";\n\nexport interface QueryBoundaryDataMapBaseProps<ItemType> {\n /**\n * The elements to show after data has been loaded.\n *\n * This is best provided as a function with a data argument that guarantees the data will not be undefined by the time you receive it here.\n */\n children: ReactNode | ((data: ItemType) => ReactNode);\n /** The component to show when the data is being fetched. */\n loadingFallback?: ReactNode;\n /** The component to show if the array is empty. */\n emptyFallback?: ReactNode;\n /** Throw an error if the provided data is not an array. (defaults to `true`) */\n strictlyRequireArray?: boolean;\n /**\n * A function that takes a data item and returns the key to be used for the item.\n *\n * If not provided, it will fall back to using the index.\n */\n itemKey?: (item: ItemType, index: number) => Key;\n}\n\nexport interface QueryBoundaryDataMapPropsWithItemParser<\n ItemType,\n> extends QueryBoundaryDataMapBaseProps<ItemType> {\n /** A parser for each data item. */\n itemParser: (data: unknown) => ItemType;\n dataParser?: never;\n}\n\nexport interface QueryBoundaryDataMapPropsWithDataParser<\n ItemType,\n> extends QueryBoundaryDataMapBaseProps<ItemType> {\n /** A parser for each data item. */\n dataParser: (data: unknown) => Array<ItemType>;\n itemParser?: never;\n}\n\nexport interface QueryBoundaryDataMapPropsWithNoParser<\n ItemType,\n> extends QueryBoundaryDataMapBaseProps<ItemType> {\n dataParser?: never;\n itemParser?: never;\n}\n\nexport type QueryBoundaryDataMapProps<ItemType> =\n | QueryBoundaryDataMapPropsWithItemParser<ItemType>\n | QueryBoundaryDataMapPropsWithDataParser<ItemType>\n | QueryBoundaryDataMapPropsWithNoParser<ItemType>;\n\n/**\n * The component responsible for handling an array of data provided by `QueryBoundaryProvider`.\n *\n * It will map through the data array, rendering the result of the children function in a fragment with a key of its index in the list, unless overridden by the `itemKey` prop.\n *\n * @template ItemType - The type of data being loaded.\n *\n * @throws {DataError} If the data provided by `QueryBoundaryProvider` is not an array, and the `strictlyRequireArray` prop is `true` (it is by default).\n */\nfunction QueryBoundaryDataMap<ItemType>({\n children,\n loadingFallback = <CircularProgress />,\n itemKey,\n itemParser,\n dataParser,\n emptyFallback = <Typography>No data present</Typography>,\n strictlyRequireArray = true,\n}: QueryBoundaryDataMapProps<ItemType>) {\n const { isLoading, data, error } = useQueryBoundaryContext<Array<ItemType>>();\n\n if (isLoading) {\n return <>{loadingFallback}</>;\n }\n\n if (error) {\n return null;\n }\n\n if (data === null || data === undefined) {\n return null;\n }\n\n if (!Array.isArray(data)) {\n if (strictlyRequireArray) {\n throw new DataError(\n { data, strictlyRequireArray },\n \"NOT_AN_ARRAY\",\n \"Expected the data to be an array but it was not an array.\",\n );\n }\n return null;\n }\n\n if (data.length === 0) {\n return <>{emptyFallback}</>;\n }\n\n let items: Array<ItemType>;\n\n if (dataParser) {\n items = dataParser(data);\n } else if (itemParser) {\n items = data.map(itemParser);\n } else {\n items = data;\n }\n\n return (\n <>\n {items.map((item, index) => {\n return (\n <Fragment key={itemKey ? itemKey(item, index) : index}>\n {typeof children === \"function\" ? children(item) : children}\n </Fragment>\n );\n })}\n </>\n );\n}\n\nexport default QueryBoundaryDataMap;\n","import type { ReactNode } from \"react\";\n\nimport Alert from \"@mui/material/Alert\";\nimport { useRef } from \"react\";\n\nimport { useQueryBoundaryContext } from \"src/QueryBoundary/QueryBoundaryProvider\";\n\nexport interface QueryBoundaryErrorProps {\n /** The component to show if an error has been thrown. */\n children?: ReactNode | ((error: unknown) => ReactNode);\n /** An option to log the error to the console. */\n logError?: boolean;\n}\n\n/**\n * The component responsible for showing any errors provided by QueryBoundaryProvider.\n */\nfunction QueryBoundaryError({ children, logError }: QueryBoundaryErrorProps) {\n const { data, error } = useQueryBoundaryContext();\n const warnedOnceRef = useRef(false);\n\n if (error) {\n if (logError && !warnedOnceRef.current) {\n if (data !== null && data !== undefined) {\n console.error(\n \"An error has occurred but data is still present. This may indicate an invalid query state.\",\n { data, error },\n );\n } else {\n console.error(error);\n }\n warnedOnceRef.current = true;\n }\n if (typeof children === \"function\") {\n return children(error);\n }\n if (children !== undefined) {\n return <>{children}</>;\n }\n\n return (\n <Alert severity=\"error\">\n {typeof error === \"object\" && \"message\" in error && typeof error.message === \"string\"\n ? error.message\n : \"An unknown error has occured.\"}\n </Alert>\n );\n }\n\n return null;\n}\n\nexport default QueryBoundaryError;\n","import type { ReactNode } from \"react\";\n\nimport Typography from \"@mui/material/Typography\";\n\nimport { useQueryBoundaryContext } from \"src/QueryBoundary/QueryBoundaryProvider\";\n\nexport interface QueryBoundaryNullablePropsWithUndefinedOrNull {\n /** The component to show if no error was thrown but data is undefined */\n undefinedFallback?: ReactNode;\n /** The component to show if no error was thrown but data is null */\n nullFallback?: ReactNode;\n nullableFallback?: never;\n}\n\nexport interface QueryBoundaryNullablePropsWithNullable {\n undefinedFallback?: never;\n nullFallback?: never;\n /** The component to show if no error was thrown but data is undefined or null */\n nullableFallback?: ReactNode;\n}\n\nexport type QueryBoundaryNullableProps =\n | QueryBoundaryNullablePropsWithUndefinedOrNull\n | QueryBoundaryNullablePropsWithNullable;\n\n/** The component responsible for handling cases when the data provided by `QueryBoundaryProvider` may be missing. */\nfunction QueryBoundaryNullable({\n undefinedFallback,\n nullFallback,\n nullableFallback,\n}: QueryBoundaryNullableProps) {\n const { isLoading, data, error } = useQueryBoundaryContext();\n\n if (isLoading) {\n return null;\n }\n\n if (error) {\n return null;\n }\n\n if (data === null || data === undefined) {\n if (nullableFallback !== undefined) {\n return <>{nullableFallback}</>;\n }\n\n if (data === undefined) {\n if (undefinedFallback !== undefined) {\n return <>{undefinedFallback}</>;\n }\n return <Typography>No data available.</Typography>;\n }\n\n if (data === null) {\n if (nullFallback !== undefined) {\n return <>{nullFallback}</>;\n }\n return <Typography>No data found.</Typography>;\n }\n }\n\n return null;\n}\n\nexport default QueryBoundaryNullable;\n","import type { ReactNode } from \"react\";\n\nimport type { QueryBoundaryErrorProps } from \"src/QueryBoundary/QueryBoundaryError\";\nimport type { QueryBoundaryNullableProps } from \"src/QueryBoundary/QueryBoundaryNullable\";\n\nimport QueryBoundaryError from \"src/QueryBoundary/QueryBoundaryError\";\nimport QueryBoundaryNullable from \"src/QueryBoundary/QueryBoundaryNullable\";\n\nexport type QueryBoundaryFallbackProps = Omit<QueryBoundaryErrorProps, \"children\"> & {\n /** The component to show if an error has been thrown. */\n errorFallback?: ReactNode | ((error: unknown) => ReactNode);\n} & QueryBoundaryNullableProps;\n\n/**\n * The component responsible for handling both errors and nullable data from `QueryBoundaryProvider`\n */\nfunction QueryBoundaryFallback({\n errorFallback,\n logError,\n ...queryBoundaryNullableProps\n}: QueryBoundaryFallbackProps) {\n return (\n <>\n <QueryBoundaryError logError={logError}>{errorFallback}</QueryBoundaryError>\n <QueryBoundaryNullable {...queryBoundaryNullableProps} />\n </>\n );\n}\n\nexport default QueryBoundaryFallback;\n","import type { JSX, ReactNode } from \"react\";\n\nimport QueryBoundaryError from \"src/QueryBoundary/QueryBoundaryError\";\nimport QueryBoundaryFallback from \"src/QueryBoundary/QueryBoundaryFallback\";\nimport QueryBoundaryNullable from \"src/QueryBoundary/QueryBoundaryNullable\";\nimport QueryBoundaryProvider from \"src/QueryBoundary/QueryBoundaryProvider\";\n\nexport interface QueryBase<DataType> {\n /** The current loading status (true if loading, false if not) */\n isLoading?: boolean;\n /** The error given if the response gave an error. */\n error?: unknown;\n /** The data being loaded. */\n data: DataType;\n}\n\nexport interface CreateBaseQueryBoundaryParameters<DataType> {\n query: QueryBase<DataType>;\n}\n\nexport interface DefaultQueryBoundaryComponentsBase {\n /** Provides the context for the query boundary. */\n Context: (props: { children: ReactNode }) => JSX.Element;\n /** The component responsible for showing any errors provided by `QueryBoundary.Context`. */\n Error: typeof QueryBoundaryError;\n /** The component responsible for handling cases when the data provided by `QueryBoundary.Context` may be missing. */\n Fallback: typeof QueryBoundaryFallback;\n /** The component responsible for handling both errors and nullable data from `QueryBoundary.Context`*/\n Nullable: typeof QueryBoundaryNullable;\n}\n\n/** A creator function to create the base system of QueryBoundary components with the data fully typed throughout. */\nfunction createBaseQueryBoundary<DataType>({\n query,\n}: CreateBaseQueryBoundaryParameters<DataType>): DefaultQueryBoundaryComponentsBase {\n return {\n Context: ({ children }) => {\n return (\n <QueryBoundaryProvider isLoading={query.isLoading} error={query.error} data={query.data}>\n {children}\n </QueryBoundaryProvider>\n );\n },\n Error: QueryBoundaryError,\n Fallback: QueryBoundaryFallback,\n Nullable: QueryBoundaryNullable,\n };\n}\n\nexport default createBaseQueryBoundary;\n","import type {\n DefaultQueryBoundaryComponentsBase,\n QueryBase,\n} from \"src/QueryBoundary/creators/createBaseQueryBoundary\";\n\nimport createBaseQueryBoundary from \"src/QueryBoundary/creators/createBaseQueryBoundary\";\nimport QueryBoundaryData from \"src/QueryBoundary/QueryBoundaryData\";\n\nexport interface QueryItem<DataType> extends Omit<QueryBase<DataType>, \"data\"> {\n /** The data being loaded. */\n data: DataType | null | undefined;\n}\n\nexport interface CreateItemQueryBoundaryParameters<DataType> {\n query: QueryItem<DataType>;\n}\n\nexport interface DefaultQueryBoundaryItemComponents<\n DataType,\n> extends DefaultQueryBoundaryComponentsBase {\n /**\n * The component responsible for showing the data provided by `QueryBoundary.Context`.\n *\n * @template DataType - The type of data being loaded.\n */\n Data: typeof QueryBoundaryData<DataType>;\n}\n\n/** A creator function to create the system of QueryBoundary components with the data treated as a single data item, fully typed throughout. */\nfunction createItemQueryBoundary<DataType>({\n query,\n}: CreateItemQueryBoundaryParameters<DataType>): DefaultQueryBoundaryItemComponents<DataType> {\n const baseComponents = createBaseQueryBoundary({ query });\n\n return {\n ...baseComponents,\n Data: QueryBoundaryData,\n };\n}\n\nexport default createItemQueryBoundary;\n","import type { ReactNode } from \"react\";\n\nimport type { QueryBoundaryDataProps } from \"src/QueryBoundary/QueryBoundaryData\";\nimport type { QueryBoundaryFallbackProps } from \"src/QueryBoundary/QueryBoundaryFallback\";\nimport type { QueryBoundaryContextValue } from \"src/QueryBoundary/QueryBoundaryProvider\";\n\nimport CircularProgress from \"@mui/material/CircularProgress\";\n\nimport createItemQueryBoundary from \"src/QueryBoundary/creators/createItemQueryBoundary\";\n\nexport type QueryBoundaryItemWrapperProps<DataType> = QueryBoundaryContextValue<DataType> &\n QueryBoundaryFallbackProps &\n QueryBoundaryDataProps<DataType>;\n\n/**\n * An in-line component that deals with state management when fetching data from an API.\n * This may be used over QueryBoundaryProvider if you don't require as much control over the placement of the error message and data display.\n *\n * @template DataType - The type of data being loaded.\n */\nfunction QueryBoundaryItemWrapper<DataType>({\n children,\n errorFallback,\n undefinedFallback,\n nullFallback,\n nullableFallback,\n logError,\n loadingFallback = <CircularProgress />,\n isLoading,\n error,\n data,\n dataParser,\n}: QueryBoundaryItemWrapperProps<DataType>) {\n const QueryBoundary = createItemQueryBoundary({ query: { isLoading, error, data } });\n\n let boundaryFallbackComponent: ReactNode = (\n <QueryBoundary.Fallback logError={logError} errorFallback={errorFallback} />\n );\n\n if (nullableFallback !== undefined) {\n boundaryFallbackComponent = (\n <QueryBoundary.Fallback\n nullableFallback={nullableFallback}\n logError={logError}\n errorFallback={errorFallback}\n />\n );\n } else if (undefinedFallback !== undefined || nullFallback !== undefined) {\n boundaryFallbackComponent = (\n <QueryBoundary.Fallback\n undefinedFallback={undefinedFallback}\n nullFallback={nullFallback}\n logError={logError}\n errorFallback={errorFallback}\n />\n );\n }\n\n return (\n <QueryBoundary.Context>\n {boundaryFallbackComponent}\n <QueryBoundary.Data loadingFallback={loadingFallback} dataParser={dataParser}>\n {children}\n </QueryBoundary.Data>\n </QueryBoundary.Context>\n );\n}\n\nexport default QueryBoundaryItemWrapper;\n","import type {\n DefaultQueryBoundaryComponentsBase,\n QueryBase,\n} from \"src/QueryBoundary/creators/createBaseQueryBoundary\";\n\nimport createBaseQueryBoundary from \"src/QueryBoundary/creators/createBaseQueryBoundary\";\nimport QueryBoundaryDataMap from \"src/QueryBoundary/QueryBoundaryDataMap\";\n\nexport interface QueryList<DataType> extends Omit<QueryBase<DataType>, \"data\"> {\n /** The data being loaded. */\n data: Array<DataType> | null | undefined;\n}\n\nexport interface CreateListQueryBoundaryParameters<DataType> {\n query: QueryList<DataType>;\n}\n\nexport interface DefaultQueryBoundaryListComponents<\n DataType,\n> extends DefaultQueryBoundaryComponentsBase {\n /**\n * The component responsible for handling an array of data provided by `QueryBoundary.Context`.\n *\n * It will map through the data array, rendering the result of the children function in a fragment with a key of its index in the list, unless overridden by the `itemKey` prop.\n *\n * @template ItemType - The type of data being loaded.\n *\n * @throws {DataError} If the data provided by `QueryBoundary.Context` is not an array, and the `strictlyRequireArray` prop is `true` (it is by default).\n */\n DataMap: typeof QueryBoundaryDataMap<DataType>;\n}\n\n/** A creator function to create the system of QueryBoundary components with the data treated as an array of data items, fully typed throughout. */\nfunction createListQueryBoundary<DataType>({\n query,\n}: CreateListQueryBoundaryParameters<DataType>): DefaultQueryBoundaryListComponents<DataType> {\n const baseComponents = createBaseQueryBoundary({ query });\n\n return {\n ...baseComponents,\n DataMap: QueryBoundaryDataMap,\n };\n}\n\nexport default createListQueryBoundary;\n","import type { ReactNode } from \"react\";\n\nimport type { QueryBoundaryDataMapProps } from \"src/QueryBoundary/QueryBoundaryDataMap\";\nimport type { QueryBoundaryFallbackProps } from \"src/QueryBoundary/QueryBoundaryFallback\";\nimport type { QueryBoundaryContextValue } from \"src/QueryBoundary/QueryBoundaryProvider\";\n\nimport createListQueryBoundary from \"src/QueryBoundary/creators/createListQueryBoundary\";\n\nexport type QueryBoundaryListWrapperProps<ItemType> = QueryBoundaryContextValue<Array<ItemType>> &\n QueryBoundaryFallbackProps &\n QueryBoundaryDataMapProps<ItemType>;\n\n/**\n * An in-line component that handles an array of data provided by `QueryBoundaryProvider`.\n *\n * This may be used over QueryBoundaryProvider/QueryBoundaryDataMap if you don't require as much control over the placement of the error message and data display.\n *\n * @template DataType - The type of data being loaded.\n */\nfunction QueryBoundaryListWrapper<ItemType>({\n loadingFallback,\n undefinedFallback,\n nullFallback,\n nullableFallback,\n logError,\n errorFallback,\n children,\n isLoading,\n error,\n data,\n dataParser,\n itemParser,\n itemKey,\n}: QueryBoundaryListWrapperProps<ItemType>) {\n const QueryBoundary = createListQueryBoundary({ query: { isLoading, error, data } });\n\n let boundaryErrorFallback: ReactNode = (\n <QueryBoundary.Fallback logError={logError} errorFallback={errorFallback} />\n );\n\n if (nullableFallback !== undefined) {\n boundaryErrorFallback = (\n <QueryBoundary.Fallback\n nullableFallback={nullableFallback}\n logError={logError}\n errorFallback={errorFallback}\n />\n );\n } else if (undefinedFallback !== undefined || nullFallback !== undefined) {\n boundaryErrorFallback = (\n <QueryBoundary.Fallback\n undefinedFallback={undefinedFallback}\n nullFallback={nullFallback}\n logError={logError}\n errorFallback={errorFallback}\n />\n );\n }\n\n let boundaryDataMapComponent: ReactNode = (\n <QueryBoundary.DataMap loadingFallback={loadingFallback} itemKey={itemKey}>\n {children}\n </QueryBoundary.DataMap>\n );\n\n if (dataParser) {\n boundaryDataMapComponent = (\n <QueryBoundary.DataMap\n loadingFallback={loadingFallback}\n itemKey={itemKey}\n dataParser={dataParser}\n >\n {children}\n </QueryBoundary.DataMap>\n );\n } else if (itemParser) {\n boundaryDataMapComponent = (\n <QueryBoundary.DataMap\n loadingFallback={loadingFallback}\n itemKey={itemKey}\n itemParser={itemParser}\n >\n {children}\n </QueryBoundary.DataMap>\n );\n }\n\n return (\n <QueryBoundary.Context>\n {boundaryErrorFallback}\n {boundaryDataMapComponent}\n </QueryBoundary.Context>\n );\n}\n\nexport default QueryBoundaryListWrapper;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/QueryBoundary/QueryBoundaryProvider.tsx","../../src/QueryBoundary/QueryBoundaryData.tsx","../../src/QueryBoundary/QueryBoundaryDataMap.tsx","../../src/QueryBoundary/QueryBoundaryError.tsx","../../src/QueryBoundary/QueryBoundaryNullable.tsx","../../src/QueryBoundary/QueryBoundaryFallback.tsx","../../src/QueryBoundary/creators/createBaseQueryBoundary.tsx","../../src/QueryBoundary/creators/createItemQueryBoundary.tsx","../../src/QueryBoundary/QueryBoundaryItemWrapper.tsx","../../src/QueryBoundary/creators/createListQueryBoundary.tsx","../../src/QueryBoundary/QueryBoundaryListWrapper.tsx"],"sourcesContent":["import type { OptionalOnCondition } from \"@alextheman/utility\";\nimport type { ReactNode } from \"react\";\n\nimport type { ContextHookOptions } from \"src/root/types\";\n\nimport { DataError } from \"@alextheman/utility/v6\";\nimport { createContext, use } from \"react\";\n\nexport interface QueryBoundaryContextValue<DataType> {\n /** The current loading status (true if loading, false if not) */\n isLoading?: boolean;\n /** The data being loaded. */\n data?: DataType | null | undefined;\n /** The error given if the request gave an error. */\n error?: unknown;\n}\n\nexport type QueryBoundaryProviderProps<DataType> = QueryBoundaryContextValue<DataType> & {\n children: ReactNode;\n};\n\nconst QueryBoundaryContext = createContext<QueryBoundaryContextValue<unknown> | undefined>(\n undefined,\n);\n\n/** Access the QueryBoundary context directly. */\nexport function useQueryBoundaryContext<DataType, Strict extends boolean = true>({\n strict = true as Strict,\n}: ContextHookOptions<Strict> = {}): OptionalOnCondition<\n Strict,\n QueryBoundaryContextValue<DataType>\n> {\n const context = use(QueryBoundaryContext);\n if (strict && !context) {\n throw new DataError(\n { strict, context },\n \"QUERY_BOUNDARY_PROVIDER_NOT_FOUND\",\n \"Could not find the QueryBoundaryProvider context. Please double-check that it is present.\",\n );\n }\n return context as OptionalOnCondition<Strict, QueryBoundaryContextValue<DataType>>;\n}\n\n/**\n * A provider for a context that deals with state management when fetching data from an API.\n * This may be used over QueryBoundary if you require more control over the placement of the error message and data display.\n *\n * @template DataType - The type of data being loaded.\n */\nfunction QueryBoundaryProvider<DataType>({\n children,\n ...contextProps\n}: QueryBoundaryProviderProps<DataType>) {\n return <QueryBoundaryContext value={contextProps}>{children}</QueryBoundaryContext>;\n}\n\nexport default QueryBoundaryProvider;\n","import type { ReactNode } from \"react\";\n\nimport CircularProgress from \"@mui/material/CircularProgress\";\n\nimport { useQueryBoundaryContext } from \"src/QueryBoundary/QueryBoundaryProvider\";\n\nexport interface QueryBoundaryDataProps<DataType> {\n /**\n * The elements to show after data has been loaded.\n * This is best provided as a function with a data argument that guarantees the data will not be undefined by the time you receive it here.\n */\n children: ReactNode | ((data: NonNullable<DataType>) => ReactNode);\n /** A parser for the data. */\n dataParser?: (data: unknown) => NonNullable<DataType>;\n /** The component to show when the data is being fetched. */\n loadingFallback?: ReactNode;\n}\n\n/**\n * The component responsible for showing the data provided by QueryBoundaryProvider.\n *\n * @template DataType - The type of data being loaded.\n */\nfunction QueryBoundaryData<DataType>({\n children,\n dataParser,\n loadingFallback = <CircularProgress />,\n}: QueryBoundaryDataProps<DataType>) {\n const { isLoading, data, error } = useQueryBoundaryContext<DataType>();\n\n if (error) {\n return null;\n }\n\n if (isLoading) {\n return <>{loadingFallback}</>;\n }\n\n if (data === null || data === undefined) {\n return null;\n }\n\n return (\n <>\n {typeof children === \"function\" ? children(dataParser ? dataParser(data) : data) : children}\n </>\n );\n}\n\nexport default QueryBoundaryData;\n","import type { Key, ReactNode } from \"react\";\n\nimport { DataError } from \"@alextheman/utility/v6\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport Typography from \"@mui/material/Typography\";\nimport { Fragment } from \"react\";\n\nimport { useQueryBoundaryContext } from \"src/QueryBoundary/QueryBoundaryProvider\";\n\nexport interface QueryBoundaryDataMapBaseProps<ItemType> {\n /**\n * The elements to show after data has been loaded.\n *\n * This is best provided as a function with a data argument that guarantees the data will not be undefined by the time you receive it here.\n */\n children: ReactNode | ((data: ItemType) => ReactNode);\n /** The component to show when the data is being fetched. */\n loadingFallback?: ReactNode;\n /** The component to show if the array is empty. */\n emptyFallback?: ReactNode;\n /** Throw an error if the provided data is not an array. (defaults to `true`) */\n strictlyRequireArray?: boolean;\n /**\n * A function that takes a data item and returns the key to be used for the item.\n *\n * If not provided, it will fall back to using the index.\n */\n itemKey?: (item: ItemType, index: number) => Key;\n}\n\nexport interface QueryBoundaryDataMapPropsWithItemParser<\n ItemType,\n> extends QueryBoundaryDataMapBaseProps<ItemType> {\n /** A parser for each data item. */\n itemParser: (data: unknown) => ItemType;\n dataParser?: never;\n}\n\nexport interface QueryBoundaryDataMapPropsWithDataParser<\n ItemType,\n> extends QueryBoundaryDataMapBaseProps<ItemType> {\n /** A parser for each data item. */\n dataParser: (data: unknown) => Array<ItemType>;\n itemParser?: never;\n}\n\nexport interface QueryBoundaryDataMapPropsWithNoParser<\n ItemType,\n> extends QueryBoundaryDataMapBaseProps<ItemType> {\n dataParser?: never;\n itemParser?: never;\n}\n\nexport type QueryBoundaryDataMapProps<ItemType> =\n | QueryBoundaryDataMapPropsWithItemParser<ItemType>\n | QueryBoundaryDataMapPropsWithDataParser<ItemType>\n | QueryBoundaryDataMapPropsWithNoParser<ItemType>;\n\n/**\n * The component responsible for handling an array of data provided by `QueryBoundaryProvider`.\n *\n * It will map through the data array, rendering the result of the children function in a fragment with a key of its index in the list, unless overridden by the `itemKey` prop.\n *\n * @template ItemType - The type of data being loaded.\n *\n * @throws {DataError} If the data provided by `QueryBoundaryProvider` is not an array, and the `strictlyRequireArray` prop is `true` (it is by default).\n */\nfunction QueryBoundaryDataMap<ItemType>({\n children,\n loadingFallback = <CircularProgress />,\n itemKey,\n itemParser,\n dataParser,\n emptyFallback = <Typography>No data present</Typography>,\n strictlyRequireArray = true,\n}: QueryBoundaryDataMapProps<ItemType>) {\n const { isLoading, data, error } = useQueryBoundaryContext<Array<ItemType>>();\n\n if (isLoading) {\n return <>{loadingFallback}</>;\n }\n\n if (error) {\n return null;\n }\n\n if (data === null || data === undefined) {\n return null;\n }\n\n if (!Array.isArray(data)) {\n if (strictlyRequireArray) {\n throw new DataError(\n { data, strictlyRequireArray },\n \"NOT_AN_ARRAY\",\n \"Expected the data to be an array but it was not an array.\",\n );\n }\n return null;\n }\n\n if (data.length === 0) {\n return <>{emptyFallback}</>;\n }\n\n let items: Array<ItemType>;\n\n if (dataParser) {\n items = dataParser(data);\n } else if (itemParser) {\n items = data.map(itemParser);\n } else {\n items = data;\n }\n\n return (\n <>\n {items.map((item, index) => {\n return (\n <Fragment key={itemKey ? itemKey(item, index) : index}>\n {typeof children === \"function\" ? children(item) : children}\n </Fragment>\n );\n })}\n </>\n );\n}\n\nexport default QueryBoundaryDataMap;\n","import type { ReactNode } from \"react\";\n\nimport Alert from \"@mui/material/Alert\";\nimport { useRef } from \"react\";\n\nimport { useQueryBoundaryContext } from \"src/QueryBoundary/QueryBoundaryProvider\";\n\nexport interface QueryBoundaryErrorProps {\n /** The component to show if an error has been thrown. */\n children?: ReactNode | ((error: unknown) => ReactNode);\n /** An option to log the error to the console. */\n logError?: boolean;\n}\n\n/**\n * The component responsible for showing any errors provided by QueryBoundaryProvider.\n */\nfunction QueryBoundaryError({ children, logError }: QueryBoundaryErrorProps) {\n const { data, error } = useQueryBoundaryContext();\n const warnedOnceRef = useRef(false);\n\n if (error) {\n if (logError && !warnedOnceRef.current) {\n if (data !== null && data !== undefined) {\n console.error(\n \"An error has occurred but data is still present. This may indicate an invalid query state.\",\n { data, error },\n );\n } else {\n console.error(error);\n }\n warnedOnceRef.current = true;\n }\n if (typeof children === \"function\") {\n return children(error);\n }\n if (children !== undefined) {\n return <>{children}</>;\n }\n\n return (\n <Alert severity=\"error\">\n {typeof error === \"object\" && \"message\" in error && typeof error.message === \"string\"\n ? error.message\n : \"An unknown error has occured.\"}\n </Alert>\n );\n }\n\n return null;\n}\n\nexport default QueryBoundaryError;\n","import type { ReactNode } from \"react\";\n\nimport Typography from \"@mui/material/Typography\";\n\nimport { useQueryBoundaryContext } from \"src/QueryBoundary/QueryBoundaryProvider\";\n\nexport interface QueryBoundaryNullablePropsWithUndefinedOrNull {\n /** The component to show if no error was thrown but data is undefined */\n undefinedFallback?: ReactNode;\n /** The component to show if no error was thrown but data is null */\n nullFallback?: ReactNode;\n nullableFallback?: never;\n}\n\nexport interface QueryBoundaryNullablePropsWithNullable {\n undefinedFallback?: never;\n nullFallback?: never;\n /** The component to show if no error was thrown but data is undefined or null */\n nullableFallback?: ReactNode;\n}\n\nexport type QueryBoundaryNullableProps =\n | QueryBoundaryNullablePropsWithUndefinedOrNull\n | QueryBoundaryNullablePropsWithNullable;\n\n/** The component responsible for handling cases when the data provided by `QueryBoundaryProvider` may be missing. */\nfunction QueryBoundaryNullable({\n undefinedFallback,\n nullFallback,\n nullableFallback,\n}: QueryBoundaryNullableProps) {\n const { isLoading, data, error } = useQueryBoundaryContext();\n\n if (isLoading) {\n return null;\n }\n\n if (error) {\n return null;\n }\n\n if (data === null || data === undefined) {\n if (nullableFallback !== undefined) {\n return <>{nullableFallback}</>;\n }\n\n if (data === undefined) {\n if (undefinedFallback !== undefined) {\n return <>{undefinedFallback}</>;\n }\n return <Typography>No data available.</Typography>;\n }\n\n if (data === null) {\n if (nullFallback !== undefined) {\n return <>{nullFallback}</>;\n }\n return <Typography>No data found.</Typography>;\n }\n }\n\n return null;\n}\n\nexport default QueryBoundaryNullable;\n","import type { ReactNode } from \"react\";\n\nimport type { QueryBoundaryErrorProps } from \"src/QueryBoundary/QueryBoundaryError\";\nimport type { QueryBoundaryNullableProps } from \"src/QueryBoundary/QueryBoundaryNullable\";\n\nimport QueryBoundaryError from \"src/QueryBoundary/QueryBoundaryError\";\nimport QueryBoundaryNullable from \"src/QueryBoundary/QueryBoundaryNullable\";\n\nexport type QueryBoundaryFallbackProps = Omit<QueryBoundaryErrorProps, \"children\"> & {\n /** The component to show if an error has been thrown. */\n errorFallback?: ReactNode | ((error: unknown) => ReactNode);\n} & QueryBoundaryNullableProps;\n\n/**\n * The component responsible for handling both errors and nullable data from `QueryBoundaryProvider`\n */\nfunction QueryBoundaryFallback({\n errorFallback,\n logError,\n ...queryBoundaryNullableProps\n}: QueryBoundaryFallbackProps) {\n return (\n <>\n <QueryBoundaryError logError={logError}>{errorFallback}</QueryBoundaryError>\n <QueryBoundaryNullable {...queryBoundaryNullableProps} />\n </>\n );\n}\n\nexport default QueryBoundaryFallback;\n","import type { JSX, ReactNode } from \"react\";\n\nimport QueryBoundaryError from \"src/QueryBoundary/QueryBoundaryError\";\nimport QueryBoundaryFallback from \"src/QueryBoundary/QueryBoundaryFallback\";\nimport QueryBoundaryNullable from \"src/QueryBoundary/QueryBoundaryNullable\";\nimport QueryBoundaryProvider from \"src/QueryBoundary/QueryBoundaryProvider\";\n\nexport interface QueryBase<DataType> {\n /** The current loading status (true if loading, false if not) */\n isLoading?: boolean;\n /** The error given if the response gave an error. */\n error?: unknown;\n /** The data being loaded. */\n data: DataType;\n}\n\nexport interface CreateBaseQueryBoundaryParameters<DataType> {\n query: QueryBase<DataType>;\n}\n\nexport interface DefaultQueryBoundaryComponentsBase {\n /** Provides the context for the query boundary. */\n Context: (props: { children: ReactNode }) => JSX.Element;\n /** The component responsible for showing any errors provided by `QueryBoundary.Context`. */\n Error: typeof QueryBoundaryError;\n /** The component responsible for handling cases when the data provided by `QueryBoundary.Context` may be missing. */\n Fallback: typeof QueryBoundaryFallback;\n /** The component responsible for handling both errors and nullable data from `QueryBoundary.Context`*/\n Nullable: typeof QueryBoundaryNullable;\n}\n\n/** A creator function to create the base system of QueryBoundary components with the data fully typed throughout. */\nfunction createBaseQueryBoundary<DataType>({\n query,\n}: CreateBaseQueryBoundaryParameters<DataType>): DefaultQueryBoundaryComponentsBase {\n return {\n Context: ({ children }) => {\n return (\n <QueryBoundaryProvider isLoading={query.isLoading} error={query.error} data={query.data}>\n {children}\n </QueryBoundaryProvider>\n );\n },\n Error: QueryBoundaryError,\n Fallback: QueryBoundaryFallback,\n Nullable: QueryBoundaryNullable,\n };\n}\n\nexport default createBaseQueryBoundary;\n","import type {\n DefaultQueryBoundaryComponentsBase,\n QueryBase,\n} from \"src/QueryBoundary/creators/createBaseQueryBoundary\";\n\nimport createBaseQueryBoundary from \"src/QueryBoundary/creators/createBaseQueryBoundary\";\nimport QueryBoundaryData from \"src/QueryBoundary/QueryBoundaryData\";\n\nexport interface QueryItem<DataType> extends Omit<QueryBase<DataType>, \"data\"> {\n /** The data being loaded. */\n data: DataType | null | undefined;\n}\n\nexport interface CreateItemQueryBoundaryParameters<DataType> {\n query: QueryItem<DataType>;\n}\n\nexport interface DefaultQueryBoundaryItemComponents<\n DataType,\n> extends DefaultQueryBoundaryComponentsBase {\n /**\n * The component responsible for showing the data provided by `QueryBoundary.Context`.\n *\n * @template DataType - The type of data being loaded.\n */\n Data: typeof QueryBoundaryData<DataType>;\n}\n\n/** A creator function to create the system of QueryBoundary components with the data treated as a single data item, fully typed throughout. */\nfunction createItemQueryBoundary<DataType>({\n query,\n}: CreateItemQueryBoundaryParameters<DataType>): DefaultQueryBoundaryItemComponents<DataType> {\n const baseComponents = createBaseQueryBoundary({ query });\n\n return {\n ...baseComponents,\n Data: QueryBoundaryData,\n };\n}\n\nexport default createItemQueryBoundary;\n","import type { ReactNode } from \"react\";\n\nimport type { QueryBoundaryDataProps } from \"src/QueryBoundary/QueryBoundaryData\";\nimport type { QueryBoundaryFallbackProps } from \"src/QueryBoundary/QueryBoundaryFallback\";\nimport type { QueryBoundaryContextValue } from \"src/QueryBoundary/QueryBoundaryProvider\";\n\nimport CircularProgress from \"@mui/material/CircularProgress\";\n\nimport createItemQueryBoundary from \"src/QueryBoundary/creators/createItemQueryBoundary\";\n\nexport type QueryBoundaryItemWrapperProps<DataType> = QueryBoundaryContextValue<DataType> &\n QueryBoundaryFallbackProps &\n QueryBoundaryDataProps<DataType>;\n\n/**\n * An in-line component that deals with state management when fetching data from an API.\n * This may be used over QueryBoundaryProvider if you don't require as much control over the placement of the error message and data display.\n *\n * @template DataType - The type of data being loaded.\n */\nfunction QueryBoundaryItemWrapper<DataType>({\n children,\n errorFallback,\n undefinedFallback,\n nullFallback,\n nullableFallback,\n logError,\n loadingFallback = <CircularProgress />,\n isLoading,\n error,\n data,\n dataParser,\n}: QueryBoundaryItemWrapperProps<DataType>) {\n const QueryBoundary = createItemQueryBoundary({ query: { isLoading, error, data } });\n\n let boundaryFallbackComponent: ReactNode = (\n <QueryBoundary.Fallback logError={logError} errorFallback={errorFallback} />\n );\n\n if (nullableFallback !== undefined) {\n boundaryFallbackComponent = (\n <QueryBoundary.Fallback\n nullableFallback={nullableFallback}\n logError={logError}\n errorFallback={errorFallback}\n />\n );\n } else if (undefinedFallback !== undefined || nullFallback !== undefined) {\n boundaryFallbackComponent = (\n <QueryBoundary.Fallback\n undefinedFallback={undefinedFallback}\n nullFallback={nullFallback}\n logError={logError}\n errorFallback={errorFallback}\n />\n );\n }\n\n return (\n <QueryBoundary.Context>\n {boundaryFallbackComponent}\n <QueryBoundary.Data loadingFallback={loadingFallback} dataParser={dataParser}>\n {children}\n </QueryBoundary.Data>\n </QueryBoundary.Context>\n );\n}\n\nexport default QueryBoundaryItemWrapper;\n","import type {\n DefaultQueryBoundaryComponentsBase,\n QueryBase,\n} from \"src/QueryBoundary/creators/createBaseQueryBoundary\";\n\nimport createBaseQueryBoundary from \"src/QueryBoundary/creators/createBaseQueryBoundary\";\nimport QueryBoundaryDataMap from \"src/QueryBoundary/QueryBoundaryDataMap\";\n\nexport interface QueryList<DataType> extends Omit<QueryBase<DataType>, \"data\"> {\n /** The data being loaded. */\n data: Array<DataType> | null | undefined;\n}\n\nexport interface CreateListQueryBoundaryParameters<DataType> {\n query: QueryList<DataType>;\n}\n\nexport interface DefaultQueryBoundaryListComponents<\n DataType,\n> extends DefaultQueryBoundaryComponentsBase {\n /**\n * The component responsible for handling an array of data provided by `QueryBoundary.Context`.\n *\n * It will map through the data array, rendering the result of the children function in a fragment with a key of its index in the list, unless overridden by the `itemKey` prop.\n *\n * @template ItemType - The type of data being loaded.\n *\n * @throws {DataError} If the data provided by `QueryBoundary.Context` is not an array, and the `strictlyRequireArray` prop is `true` (it is by default).\n */\n DataMap: typeof QueryBoundaryDataMap<DataType>;\n}\n\n/** A creator function to create the system of QueryBoundary components with the data treated as an array of data items, fully typed throughout. */\nfunction createListQueryBoundary<DataType>({\n query,\n}: CreateListQueryBoundaryParameters<DataType>): DefaultQueryBoundaryListComponents<DataType> {\n const baseComponents = createBaseQueryBoundary({ query });\n\n return {\n ...baseComponents,\n DataMap: QueryBoundaryDataMap,\n };\n}\n\nexport default createListQueryBoundary;\n","import type { ReactNode } from \"react\";\n\nimport type { QueryBoundaryDataMapProps } from \"src/QueryBoundary/QueryBoundaryDataMap\";\nimport type { QueryBoundaryFallbackProps } from \"src/QueryBoundary/QueryBoundaryFallback\";\nimport type { QueryBoundaryContextValue } from \"src/QueryBoundary/QueryBoundaryProvider\";\n\nimport createListQueryBoundary from \"src/QueryBoundary/creators/createListQueryBoundary\";\n\nexport type QueryBoundaryListWrapperProps<ItemType> = QueryBoundaryContextValue<Array<ItemType>> &\n QueryBoundaryFallbackProps &\n QueryBoundaryDataMapProps<ItemType>;\n\n/**\n * An in-line component that handles an array of data provided by `QueryBoundaryProvider`.\n *\n * This may be used over QueryBoundaryProvider/QueryBoundaryDataMap if you don't require as much control over the placement of the error message and data display.\n *\n * @template DataType - The type of data being loaded.\n */\nfunction QueryBoundaryListWrapper<ItemType>({\n loadingFallback,\n undefinedFallback,\n nullFallback,\n nullableFallback,\n logError,\n errorFallback,\n children,\n isLoading,\n error,\n data,\n dataParser,\n itemParser,\n itemKey,\n}: QueryBoundaryListWrapperProps<ItemType>) {\n const QueryBoundary = createListQueryBoundary({ query: { isLoading, error, data } });\n\n let boundaryErrorFallback: ReactNode = (\n <QueryBoundary.Fallback logError={logError} errorFallback={errorFallback} />\n );\n\n if (nullableFallback !== undefined) {\n boundaryErrorFallback = (\n <QueryBoundary.Fallback\n nullableFallback={nullableFallback}\n logError={logError}\n errorFallback={errorFallback}\n />\n );\n } else if (undefinedFallback !== undefined || nullFallback !== undefined) {\n boundaryErrorFallback = (\n <QueryBoundary.Fallback\n undefinedFallback={undefinedFallback}\n nullFallback={nullFallback}\n logError={logError}\n errorFallback={errorFallback}\n />\n );\n }\n\n let boundaryDataMapComponent: ReactNode = (\n <QueryBoundary.DataMap loadingFallback={loadingFallback} itemKey={itemKey}>\n {children}\n </QueryBoundary.DataMap>\n );\n\n if (dataParser) {\n boundaryDataMapComponent = (\n <QueryBoundary.DataMap\n loadingFallback={loadingFallback}\n itemKey={itemKey}\n dataParser={dataParser}\n >\n {children}\n </QueryBoundary.DataMap>\n );\n } else if (itemParser) {\n boundaryDataMapComponent = (\n <QueryBoundary.DataMap\n loadingFallback={loadingFallback}\n itemKey={itemKey}\n itemParser={itemParser}\n >\n {children}\n </QueryBoundary.DataMap>\n );\n }\n\n return (\n <QueryBoundary.Context>\n {boundaryErrorFallback}\n {boundaryDataMapComponent}\n </QueryBoundary.Context>\n );\n}\n\nexport default QueryBoundaryListWrapper;\n"],"mappings":"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"}
@@ -32,5 +32,9 @@ declare function ThemeProvider({
32
32
  themeOptions
33
33
  }: ThemeProviderProps): import("react/jsx-runtime").JSX.Element;
34
34
  //#endregion
35
- export { type ThemeContextValue, ThemeProvider, type ThemeProviderProps, useThemeContext };
35
+ //#region src/theme/ThemeToggle.d.ts
36
+ /** A toggle to switch between dark mode and light mode. Must be used in a `ThemeProvider`. */
37
+ declare function ThemeToggle(): import("react/jsx-runtime").JSX.Element;
38
+ //#endregion
39
+ export { type ThemeContextValue, ThemeProvider, type ThemeProviderProps, ThemeToggle, useThemeContext };
36
40
  //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1,2 @@
1
- import{omitProperties as e}from"@alextheman/utility";import{DataError as t}from"@alextheman/utility/v6";import n from"@mui/material/CssBaseline";import{ThemeProvider as r,createTheme as i}from"@mui/material/styles";import{createContext as a,use as o,useMemo as s,useState as c}from"react";import{jsx as l,jsxs as u}from"react/jsx-runtime";const d=a(void 0);function f({strict:e=!0}={}){let n=o(d);if(e&&!n)throw new t({strict:e,context:n},`MODE_PROVIDER_NOT_FOUND`,`Could not find the ModeProvider context. Please double-check that it is present.`);return n}function p({children:t,mode:a=`dark`,themeOptions:o}){let[f,p]=c(a),m=s(()=>i({palette:{mode:f,...e(o?.palette??{},[`mode`])},components:{MuiPaper:{styleOverrides:{root:({theme:e})=>({border:1,borderStyle:`solid`,borderColor:e.palette.divider}),...o?.components?.MuiPaper?.styleOverrides},...e(o?.components?.MuiPaper??{},`styleOverrides`)},...e(o?.components??{},`MuiPaper`)},...e(o??{},[`components`,`palette`])}),[f,o]);return l(d,{value:{mode:f,toggleMode:()=>{p(e=>e===`light`?`dark`:`light`)}},children:u(r,{theme:m,children:[l(n,{}),t]})})}export{p as ThemeProvider,f as useThemeContext};
1
+ import{omitProperties as e}from"@alextheman/utility";import{DataError as t}from"@alextheman/utility/v6";import n from"@mui/material/CssBaseline";import{ThemeProvider as r,createTheme as i,styled as a}from"@mui/material/styles";import{createContext as o,use as s,useMemo as c,useState as l}from"react";import{jsx as u,jsxs as d}from"react/jsx-runtime";import f from"@mui/material/Tooltip";import{MdOutlineDarkMode as p,MdOutlineLightMode as m}from"react-icons/md";import h from"@mui/material/Box";import g from"@mui/material/Switch";const _=o(void 0);function v({strict:e=!0}={}){let n=s(_);if(e&&!n)throw new t({strict:e,context:n},`MODE_PROVIDER_NOT_FOUND`,`Could not find the ModeProvider context. Please double-check that it is present.`);return n}function y({children:t,mode:a=`dark`,themeOptions:o}){let[s,f]=l(a),p=c(()=>i({palette:{mode:s,...e(o?.palette??{},[`mode`])},components:{MuiPaper:{styleOverrides:{root:({theme:e})=>({border:1,borderStyle:`solid`,borderColor:e.palette.divider}),...o?.components?.MuiPaper?.styleOverrides},...e(o?.components?.MuiPaper??{},`styleOverrides`)},...e(o?.components??{},`MuiPaper`)},...e(o??{},[`components`,`palette`])}),[s,o]);return u(_,{value:{mode:s,toggleMode:()=>{f(e=>e===`light`?`dark`:`light`)}},children:d(r,{theme:p,children:[u(n,{}),t]})})}const b=a(g)(()=>({padding:8,"& .MuiSwitch-track":{borderRadius:11,"&::before, &::after":{content:`""`,position:`absolute`,top:`50%`,transform:`translateY(-50%)`,fontSize:16,width:28,height:28}}}));function x({checkedIcon:e,checkedIconStyles:t,uncheckedIcon:n,uncheckedIconStyles:r,...i}){let a={borderRadius:`50%`,borderColor:`white`,backgroundColor:`white`,display:`flex`,alignItems:`center`,justifyContent:`center`,padding:.25},o={color:`black`,maxWidth:16.5,maxHeight:16.5};return u(b,{checkedIcon:u(h,{sx:a,children:u(e,{style:{...o,...t}})}),icon:u(h,{sx:a,children:u(n,{style:{...o,...r}})}),...i})}function S(){let{mode:e,toggleMode:t}=v(),n=e===`dark`,r=`Enable ${n?`light`:`dark`} mode`;return u(f,{title:r,children:u(x,{uncheckedIcon:m,checkedIcon:p,checked:n,onChange:t,"aria-label":r})})}export{y as ThemeProvider,S as ThemeToggle,v as useThemeContext};
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["MuiThemeProvider"],"sources":["../../src/theme/ThemeProvider.tsx"],"sourcesContent":["import type { OptionalOnCondition } from \"@alextheman/utility\";\nimport type { PaletteMode, ThemeOptions } from \"@mui/material/styles\";\nimport type { ReactNode } from \"react\";\n\nimport type { ContextHookOptions } from \"src/root/types\";\n\nimport { omitProperties } from \"@alextheman/utility\";\nimport { DataError } from \"@alextheman/utility/v6\";\nimport CssBaseline from \"@mui/material/CssBaseline\";\nimport { createTheme, ThemeProvider as MuiThemeProvider } from \"@mui/material/styles\";\nimport { createContext, use, useMemo, useState } from \"react\";\n\nexport interface ThemeContextValue {\n toggleMode: () => void;\n mode: PaletteMode;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | undefined>(undefined);\n\n/** Access the mode context directly. */\nexport function useThemeContext<Strict extends boolean = true>({\n strict = true as Strict,\n}: ContextHookOptions<Strict> = {}): OptionalOnCondition<Strict, ThemeContextValue> {\n const context = use(ThemeContext);\n if (strict && !context) {\n throw new DataError(\n { strict, context },\n \"MODE_PROVIDER_NOT_FOUND\",\n \"Could not find the ModeProvider context. Please double-check that it is present.\",\n );\n }\n return context as OptionalOnCondition<Strict, ThemeContextValue>;\n}\n\nexport interface ThemeProviderProps {\n /** The children that will have access to the current mode. */\n children: ReactNode;\n /** The initial mode. */\n mode?: PaletteMode;\n /** Extra options to apply on top of our default theme options */\n themeOptions?: ThemeOptions;\n}\n\n/** Provides information about the current theme mode to its children components. */\nfunction ThemeProvider({ children, mode: modeProp = \"dark\", themeOptions }: ThemeProviderProps) {\n const [mode, setMode] = useState<PaletteMode>(modeProp);\n\n const theme = useMemo(() => {\n return createTheme({\n palette: {\n mode,\n ...omitProperties(themeOptions?.palette ?? {}, [\"mode\"]),\n },\n components: {\n MuiPaper: {\n styleOverrides: {\n root: ({ theme }) => {\n return {\n border: 1,\n borderStyle: \"solid\",\n borderColor: theme.palette.divider,\n };\n },\n ...themeOptions?.components?.MuiPaper?.styleOverrides,\n },\n ...omitProperties(themeOptions?.components?.MuiPaper ?? {}, \"styleOverrides\"),\n },\n ...omitProperties(themeOptions?.components ?? {}, \"MuiPaper\"),\n },\n ...omitProperties(themeOptions ?? {}, [\"components\", \"palette\"]),\n });\n }, [mode, themeOptions]);\n\n return (\n <ThemeContext\n value={{\n mode,\n toggleMode: () => {\n setMode((prev) => {\n return prev === \"light\" ? \"dark\" : \"light\";\n });\n },\n }}\n >\n <MuiThemeProvider theme={theme}>\n <CssBaseline />\n {children}\n </MuiThemeProvider>\n </ThemeContext>\n );\n}\n\nexport default ThemeProvider;\n"],"mappings":"mVAiBA,MAAM,EAAe,EAA6C,IAAA,EAAS,EAG3E,SAAgB,EAA+C,CAC7D,SAAS,IACqB,CAAC,EAAmD,CAClF,IAAM,EAAU,EAAI,CAAY,EAChC,GAAI,GAAU,CAAC,EACb,MAAM,IAAI,EACR,CAAE,SAAQ,SAAQ,EAClB,0BACA,kFACF,EAEF,OAAO,CACT,CAYA,SAAS,EAAc,CAAE,WAAU,KAAM,EAAW,OAAQ,gBAAoC,CAC9F,GAAM,CAAC,EAAM,GAAW,EAAsB,CAAQ,EAEhD,EAAQ,MACL,EAAY,CACjB,QAAS,CACP,OACA,GAAG,EAAe,GAAc,SAAW,CAAC,EAAG,CAAC,MAAM,CAAC,CACzD,EACA,WAAY,CACV,SAAU,CACR,eAAgB,CACd,MAAO,CAAE,YACA,CACL,OAAQ,EACR,YAAa,QACb,YAAa,EAAM,QAAQ,OAC7B,GAEF,GAAG,GAAc,YAAY,UAAU,cACzC,EACA,GAAG,EAAe,GAAc,YAAY,UAAY,CAAC,EAAG,gBAAgB,CAC9E,EACA,GAAG,EAAe,GAAc,YAAc,CAAC,EAAG,UAAU,CAC9D,EACA,GAAG,EAAe,GAAgB,CAAC,EAAG,CAAC,aAAc,SAAS,CAAC,CACjE,CAAC,EACA,CAAC,EAAM,CAAY,CAAC,EAEvB,OACE,EAAC,EAAD,CACE,MAAO,CACL,OACA,eAAkB,CAChB,EAAS,GACA,IAAS,QAAU,OAAS,OACpC,CACH,CACF,WAEA,EAACA,EAAD,CAAyB,iBAAzB,CACE,EAAC,EAAD,CAAc,CAAA,EACb,CACe,GACN,CAAA,CAElB"}
1
+ {"version":3,"file":"index.js","names":["MuiThemeProvider"],"sources":["../../src/theme/ThemeProvider.tsx","../../src/root/components/SwitchWithIcons.tsx","../../src/theme/ThemeToggle.tsx"],"sourcesContent":["import type { OptionalOnCondition } from \"@alextheman/utility\";\nimport type { PaletteMode, ThemeOptions } from \"@mui/material/styles\";\nimport type { ReactNode } from \"react\";\n\nimport type { ContextHookOptions } from \"src/root/types\";\n\nimport { omitProperties } from \"@alextheman/utility\";\nimport { DataError } from \"@alextheman/utility/v6\";\nimport CssBaseline from \"@mui/material/CssBaseline\";\nimport { createTheme, ThemeProvider as MuiThemeProvider } from \"@mui/material/styles\";\nimport { createContext, use, useMemo, useState } from \"react\";\n\nexport interface ThemeContextValue {\n toggleMode: () => void;\n mode: PaletteMode;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | undefined>(undefined);\n\n/** Access the mode context directly. */\nexport function useThemeContext<Strict extends boolean = true>({\n strict = true as Strict,\n}: ContextHookOptions<Strict> = {}): OptionalOnCondition<Strict, ThemeContextValue> {\n const context = use(ThemeContext);\n if (strict && !context) {\n throw new DataError(\n { strict, context },\n \"MODE_PROVIDER_NOT_FOUND\",\n \"Could not find the ModeProvider context. Please double-check that it is present.\",\n );\n }\n return context as OptionalOnCondition<Strict, ThemeContextValue>;\n}\n\nexport interface ThemeProviderProps {\n /** The children that will have access to the current mode. */\n children: ReactNode;\n /** The initial mode. */\n mode?: PaletteMode;\n /** Extra options to apply on top of our default theme options */\n themeOptions?: ThemeOptions;\n}\n\n/** Provides information about the current theme mode to its children components. */\nfunction ThemeProvider({ children, mode: modeProp = \"dark\", themeOptions }: ThemeProviderProps) {\n const [mode, setMode] = useState<PaletteMode>(modeProp);\n\n const theme = useMemo(() => {\n return createTheme({\n palette: {\n mode,\n ...omitProperties(themeOptions?.palette ?? {}, [\"mode\"]),\n },\n components: {\n MuiPaper: {\n styleOverrides: {\n root: ({ theme }) => {\n return {\n border: 1,\n borderStyle: \"solid\",\n borderColor: theme.palette.divider,\n };\n },\n ...themeOptions?.components?.MuiPaper?.styleOverrides,\n },\n ...omitProperties(themeOptions?.components?.MuiPaper ?? {}, \"styleOverrides\"),\n },\n ...omitProperties(themeOptions?.components ?? {}, \"MuiPaper\"),\n },\n ...omitProperties(themeOptions ?? {}, [\"components\", \"palette\"]),\n });\n }, [mode, themeOptions]);\n\n return (\n <ThemeContext\n value={{\n mode,\n toggleMode: () => {\n setMode((prev) => {\n return prev === \"light\" ? \"dark\" : \"light\";\n });\n },\n }}\n >\n <MuiThemeProvider theme={theme}>\n <CssBaseline />\n {children}\n </MuiThemeProvider>\n </ThemeContext>\n );\n}\n\nexport default ThemeProvider;\n","import type { CommonProps } from \"@mui/material/OverridableComponent\";\nimport type { SwitchProps } from \"@mui/material/Switch\";\nimport type { ComponentType, CSSProperties } from \"react\";\n\nimport Box from \"@mui/material/Box\";\nimport { styled } from \"@mui/material/styles\";\nimport Switch from \"@mui/material/Switch\";\n\nexport interface SwitchWithIconsProps extends Omit<SwitchProps, \"icon\" | \"checkedIcon\"> {\n /** The icon to show when the switch is in a checked state. */\n checkedIcon: ComponentType<{ style?: CSSProperties }>;\n /** Additional styling to apply to the icon that shows when checked. */\n checkedIconStyles?: CommonProps[\"style\"];\n /** The icon to show when the switch is in an unchecked state. */\n uncheckedIcon: ComponentType<{ style?: CSSProperties }>;\n /** Additional styling to apply to the icon that shows when unchecked. */\n uncheckedIconStyles?: CommonProps[\"style\"];\n}\n\nconst StyledSwitch = styled(Switch)(() => {\n return {\n padding: 8,\n \"& .MuiSwitch-track\": {\n borderRadius: 11,\n \"&::before, &::after\": {\n content: '\"\"',\n position: \"absolute\",\n top: \"50%\",\n transform: \"translateY(-50%)\",\n fontSize: 16,\n width: 28,\n height: 28,\n },\n },\n };\n});\n\n/** Renders a switch with your provided icons. */\nfunction SwitchWithIcons({\n checkedIcon: CheckedIcon,\n checkedIconStyles,\n uncheckedIcon: UncheckedIcon,\n uncheckedIconStyles,\n ...switchProps\n}: SwitchWithIconsProps) {\n const boxSx = {\n borderRadius: \"50%\",\n borderColor: \"white\",\n backgroundColor: \"white\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n padding: 0.25,\n };\n const defaultIconStyles = { color: \"black\", maxWidth: 16.5, maxHeight: 16.5 };\n return (\n <StyledSwitch\n checkedIcon={\n <Box sx={boxSx}>\n <CheckedIcon style={{ ...defaultIconStyles, ...checkedIconStyles }} />\n </Box>\n }\n icon={\n <Box sx={boxSx}>\n <UncheckedIcon style={{ ...defaultIconStyles, ...uncheckedIconStyles }} />\n </Box>\n }\n {...switchProps}\n />\n );\n}\n\nexport default SwitchWithIcons;\n","import Tooltip from \"@mui/material/Tooltip\";\nimport { MdOutlineDarkMode, MdOutlineLightMode } from \"react-icons/md\";\n\nimport SwitchWithIcons from \"src/root/components/SwitchWithIcons\";\nimport { useThemeContext } from \"src/theme/ThemeProvider\";\n\n/** A toggle to switch between dark mode and light mode. Must be used in a `ThemeProvider`. */\nfunction ThemeToggle() {\n const { mode, toggleMode } = useThemeContext();\n const isDarkMode = mode === \"dark\";\n const modeText = `Enable ${isDarkMode ? \"light\" : \"dark\"} mode`;\n\n return (\n <Tooltip title={modeText}>\n <SwitchWithIcons\n uncheckedIcon={MdOutlineLightMode}\n checkedIcon={MdOutlineDarkMode}\n checked={isDarkMode}\n onChange={toggleMode}\n aria-label={modeText}\n />\n </Tooltip>\n );\n}\n\nexport default ThemeToggle;\n"],"mappings":"ohBAiBA,MAAM,EAAe,EAA6C,IAAA,EAAS,EAG3E,SAAgB,EAA+C,CAC7D,SAAS,IACqB,CAAC,EAAmD,CAClF,IAAM,EAAU,EAAI,CAAY,EAChC,GAAI,GAAU,CAAC,EACb,MAAM,IAAI,EACR,CAAE,SAAQ,SAAQ,EAClB,0BACA,kFACF,EAEF,OAAO,CACT,CAYA,SAAS,EAAc,CAAE,WAAU,KAAM,EAAW,OAAQ,gBAAoC,CAC9F,GAAM,CAAC,EAAM,GAAW,EAAsB,CAAQ,EAEhD,EAAQ,MACL,EAAY,CACjB,QAAS,CACP,OACA,GAAG,EAAe,GAAc,SAAW,CAAC,EAAG,CAAC,MAAM,CAAC,CACzD,EACA,WAAY,CACV,SAAU,CACR,eAAgB,CACd,MAAO,CAAE,YACA,CACL,OAAQ,EACR,YAAa,QACb,YAAa,EAAM,QAAQ,OAC7B,GAEF,GAAG,GAAc,YAAY,UAAU,cACzC,EACA,GAAG,EAAe,GAAc,YAAY,UAAY,CAAC,EAAG,gBAAgB,CAC9E,EACA,GAAG,EAAe,GAAc,YAAc,CAAC,EAAG,UAAU,CAC9D,EACA,GAAG,EAAe,GAAgB,CAAC,EAAG,CAAC,aAAc,SAAS,CAAC,CACjE,CAAC,EACA,CAAC,EAAM,CAAY,CAAC,EAEvB,OACE,EAAC,EAAD,CACE,MAAO,CACL,OACA,eAAkB,CAChB,EAAS,GACA,IAAS,QAAU,OAAS,OACpC,CACH,CACF,WAEA,EAACA,EAAD,CAAyB,iBAAzB,CACE,EAAC,EAAD,CAAc,CAAA,EACb,CACe,GACN,CAAA,CAElB,CCvEA,MAAM,EAAe,EAAO,CAAM,OACzB,CACL,QAAS,EACT,qBAAsB,CACpB,aAAc,GACd,sBAAuB,CACrB,QAAS,KACT,SAAU,WACV,IAAK,MACL,UAAW,mBACX,SAAU,GACV,MAAO,GACP,OAAQ,EACV,CACF,CACF,EACD,EAGD,SAAS,EAAgB,CACvB,YAAa,EACb,oBACA,cAAe,EACf,sBACA,GAAG,GACoB,CACvB,IAAM,EAAQ,CACZ,aAAc,MACd,YAAa,QACb,gBAAiB,QACjB,QAAS,OACT,WAAY,SACZ,eAAgB,SAChB,QAAS,GACX,EACM,EAAoB,CAAE,MAAO,QAAS,SAAU,KAAM,UAAW,IAAK,EAC5E,OACE,EAAC,EAAD,CACE,YACE,EAAC,EAAD,CAAK,GAAI,WACP,EAAC,EAAD,CAAa,MAAO,CAAE,GAAG,EAAmB,GAAG,CAAkB,CAAI,CAAA,CAClE,CAAA,EAEP,KACE,EAAC,EAAD,CAAK,GAAI,WACP,EAAC,EAAD,CAAe,MAAO,CAAE,GAAG,EAAmB,GAAG,CAAoB,CAAI,CAAA,CACtE,CAAA,EAEP,GAAI,CACL,CAAA,CAEL,CC/DA,SAAS,GAAc,CACrB,GAAM,CAAE,OAAM,cAAe,EAAgB,EACvC,EAAa,IAAS,OACtB,EAAW,UAAU,EAAa,QAAU,OAAO,OAEzD,OACE,EAAC,EAAD,CAAS,MAAO,WACd,EAAC,EAAD,CACE,cAAe,EACf,YAAa,EACb,QAAS,EACT,SAAU,EACV,aAAY,CACb,CAAA,CACM,CAAA,CAEb"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alextheman/components",
3
- "version": "7.0.2",
3
+ "version": "7.0.4",
4
4
  "description": "A package containing common React components used across my projects.",
5
5
  "repository": {
6
6
  "type": "git",