diff --git a/docs/content/en/latest/references/notifications_panel/_index.md b/docs/content/en/latest/references/notifications_panel/_index.md index 05241dc5e3e..514b9f63593 100644 --- a/docs/content/en/latest/references/notifications_panel/_index.md +++ b/docs/content/en/latest/references/notifications_panel/_index.md @@ -253,17 +253,3 @@ Component for rendering loading placeholder items. | Name | Type | Description | | ---------- | ------ | ------------------------------------- | | itemHeight | number | Height of the skeleton item in pixels | - -## Custom Implementation - -If you find that the provided components and customizations are not sufficient for your use case, you can use the `useNotifications` hook to implement your own notification UI. -The hook provides methods for fetching unread/all notifications, refreshing notifications, marking them as read, and more. - -#### Props - -| Name | Type | Default | Description | -| -------------------------- | ------------------ | ------- | ------------------------------------------------------------------------ | -| backend | IAnalyticalBackend | - | Backend instance. Falls back to BackendProvider context if not specified | -| workspace | string | - | Workspace ID. Falls back to WorkspaceProvider context if not specified | -| refreshInterval (required) | number | - | Time in milliseconds between notification refreshes. Set to 0 to disable | -| itemsPerPage (required) | number | - | Number of notifications loaded in each batch | diff --git a/libs/sdk-ui-ext/api/sdk-ui-ext.api.md b/libs/sdk-ui-ext/api/sdk-ui-ext.api.md index 2bbcbc1ce18..794c57633c6 100644 --- a/libs/sdk-ui-ext/api/sdk-ui-ext.api.md +++ b/libs/sdk-ui-ext/api/sdk-ui-ext.api.md @@ -622,14 +622,6 @@ export type ISizeInfoDefault = ISizeInfo & { default: number; }; -// @public -export interface IUseNotificationsProps { - backend?: IAnalyticalBackend; - itemsPerPage: number; - refreshInterval: number; - workspace?: string; -} - // @internal (undocumented) export interface IUserEditDialogProps extends IWithTelemetryProps { // (undocumented) @@ -751,25 +743,6 @@ export type TelemetryEvent = "multiple-users-deleted" | "multiple-groups-deleted // @internal (undocumented) export type TrackEventCallback = (event: TelemetryEvent) => void; -// @public (undocumented) -export function useNotifications({ workspace, refreshInterval, itemsPerPage }: IUseNotificationsProps): { - notifications: INotification[]; - notificationsStatus: "error" | "loading" | "pending" | "success"; - notificationsError: GoodDataSdkError; - notificationsHasNextPage: boolean; - notificationsLoadNextPage: () => void; - notificationsReset: () => void; - unreadNotifications: INotification[]; - unreadNotificationsStatus: "error" | "loading" | "pending" | "success"; - unreadNotificationsError: GoodDataSdkError; - unreadNotificationsHasNextPage: boolean; - unreadNotificationsLoadNextPage: () => void; - unreadNotificationsReset: () => void; - unreadNotificationsCount: number; - markNotificationAsRead: (notificationId: string) => Promise; - markAllNotificationsAsRead: () => Promise; -}; - // @internal (undocumented) export const UserEditDialog: React_2.FC; diff --git a/libs/sdk-ui-ext/src/index.ts b/libs/sdk-ui-ext/src/index.ts index 11b611c1022..dd51dddac41 100644 --- a/libs/sdk-ui-ext/src/index.ts +++ b/libs/sdk-ui-ext/src/index.ts @@ -111,8 +111,6 @@ export * from "./internal/components/attributeHierarchies/index.js"; export * from "./internal/components/pluggableVisualizations/alerts.js"; -export { useNotifications } from "./notificationsPanel/data/useNotifications.js"; -export type { IUseNotificationsProps } from "./notificationsPanel/data/useNotifications.js"; export { NotificationsPanel } from "./notificationsPanel/NotificationsPanel/NotificationsPanel.js"; export type { INotificationsPanelProps, diff --git a/libs/sdk-ui-ext/src/internal/translations/en-US.json b/libs/sdk-ui-ext/src/internal/translations/en-US.json index 7e38e4f24fc..3d0248483e3 100644 --- a/libs/sdk-ui-ext/src/internal/translations/en-US.json +++ b/libs/sdk-ui-ext/src/internal/translations/en-US.json @@ -2087,5 +2087,20 @@ "value": "Triggered for", "comment": "", "limit": 0 + }, + "notifications.panel.error.learnMore": { + "value": "Learn more", + "comment": "", + "limit": 0 + }, + "notifications.panel.error.traceId": { + "value": "Trace ID", + "comment": "", + "limit": 0 + }, + "notifications.panel.error.message": { + "value": "The alert could not be processed.", + "comment": "", + "limit": 0 } } diff --git a/libs/sdk-ui-ext/src/notificationsPanel/Notification/AlertNotification.tsx b/libs/sdk-ui-ext/src/notificationsPanel/Notification/AlertNotification.tsx index 29ca7af3e15..2da902000d9 100644 --- a/libs/sdk-ui-ext/src/notificationsPanel/Notification/AlertNotification.tsx +++ b/libs/sdk-ui-ext/src/notificationsPanel/Notification/AlertNotification.tsx @@ -4,6 +4,7 @@ import { IAlertDescription, IAlertNotification, INotification } from "@gooddata/ import { getDateTimeConfig, IDateConfig, UiIcon } from "@gooddata/sdk-ui-kit"; import { bem } from "../bem.js"; import { Tooltip } from "../components/Tooltip.js"; +import { Popup } from "../components/Popup.js"; // import { NotificationFiltersDetail } from "../NotificationFiltersDetail/NotificationFiltersDetail.js"; import { NotificationTriggerDetail } from "../NotificationTriggersDetail/NotificationTriggersDetail.js"; import { defineMessages, FormattedDate, FormattedMessage, FormattedTime, useIntl } from "react-intl"; @@ -39,7 +40,9 @@ export function AlertNotification({ const target = event.target; const targetIsElement = target instanceof Element; const isNotificationsDetailsLink = - targetIsElement && target.closest(`[data-id="notification-detail"]`); + targetIsElement && + (target.closest(`[data-id="notification-detail"]`) || + target.closest(`[data-id="notification-error"]`)); if (isNotificationsDetailsLink) { return; } @@ -53,6 +56,11 @@ export function AlertNotification({ // const isSliced = notification.details.data.alert.attribute; // const showSeparator = filterCount && filterCount > 0 && isSliced; const notificationTitle = getNotificationTitle(notification); + const isSliced = notification.details.data.alert.attribute; + const hasTriggers = notification.details.data.alert.totalValueCount > 0; + const isError = notification.details.data.alert.status !== "SUCCESS"; + const errorMessage = notification.details.data.alert.errorMessage; + const traceId = notification.details.data.alert.traceId; return (
@@ -64,11 +72,44 @@ export function AlertNotification({
{notificationTitle}
-
- {/* - {showSeparator ? "・" : null} */} - -
+ {isError ? ( +
+
+ +
+
+ {" "} + + {errorMessage} +
+ : {traceId} +
+ } + > + {({ toggle, id }) => ( + { + toggle(); + }} + > + + + )} + +
+
+ ) : null} + {!isError && isSliced && hasTriggers ? ( +
+ {/* + {showSeparator ? "・" : null} */} + +
+ ) : null}
diff --git a/libs/sdk-ui-ext/src/notificationsPanel/Notification/DefaultNotification.scss b/libs/sdk-ui-ext/src/notificationsPanel/Notification/DefaultNotification.scss index 27bbdc80edc..fbcec835d85 100644 --- a/libs/sdk-ui-ext/src/notificationsPanel/Notification/DefaultNotification.scss +++ b/libs/sdk-ui-ext/src/notificationsPanel/Notification/DefaultNotification.scss @@ -1,4 +1,4 @@ -// (C) 2024 GoodData Corporation +// (C) 2024-2025 GoodData Corporation .gd-ui-ext-notification { $root: &; @@ -72,20 +72,19 @@ position: absolute; top: 0; right: 0; - border: 1.4px solid var(--gd-palette-complementary-0); + border: 1.4px solid var(--gd-palette-complementary-2); box-sizing: content-box; } &__details { display: flex; flex-direction: column; - justify-content: space-between; + justify-content: center; flex-shrink: 1; width: 100%; height: 100%; - - overflow: hidden; + min-width: 0; } &__title { @@ -130,5 +129,29 @@ display: flex; flex-direction: row; align-items: center; + height: 16px; + } + + &__error { + display: flex; + flex-direction: row; + align-items: center; + gap: 5px; + height: 16px; + font-size: 12px; + font-weight: 400; + color: var(--gd-palette-error-base); + white-space: nowrap; + position: relative; + } + + &__error-icon { + width: 12px; + height: 12px; + } + + &__error-popup { + font-size: 12px; + line-height: 18px; } } diff --git a/libs/sdk-ui-ext/src/notificationsPanel/NotificationsPanel/DefaultNotificationsPanel.scss b/libs/sdk-ui-ext/src/notificationsPanel/NotificationsPanel/DefaultNotificationsPanel.scss index c25a7601ac5..236c8bc8b8f 100644 --- a/libs/sdk-ui-ext/src/notificationsPanel/NotificationsPanel/DefaultNotificationsPanel.scss +++ b/libs/sdk-ui-ext/src/notificationsPanel/NotificationsPanel/DefaultNotificationsPanel.scss @@ -8,6 +8,7 @@ background-color: var(--gd-palette-complementary-0); box-shadow: 0 2px 10px 0 var(--gd-shadow-color); + border-radius: var(--gd-modal-borderRadius, 3px); display: flex; flex-direction: column; diff --git a/libs/sdk-ui-ext/src/notificationsPanel/NotificationsPanel/DefaultNotificationsPanelButton.tsx b/libs/sdk-ui-ext/src/notificationsPanel/NotificationsPanel/DefaultNotificationsPanelButton.tsx index f8b18dedd70..48f8b4b09cd 100644 --- a/libs/sdk-ui-ext/src/notificationsPanel/NotificationsPanel/DefaultNotificationsPanelButton.tsx +++ b/libs/sdk-ui-ext/src/notificationsPanel/NotificationsPanel/DefaultNotificationsPanelButton.tsx @@ -3,6 +3,7 @@ import React from "react"; import cx from "classnames"; import { UiIcon } from "@gooddata/sdk-ui-kit"; import { bem } from "../bem.js"; +import { useIsDarkTheme } from "@gooddata/sdk-ui-theme-provider"; const { b, e } = bem("gd-ui-ext-notifications-panel-button"); @@ -52,6 +53,7 @@ export function DefaultNotificationsPanelButton({ toggleNotificationPanel, hasUnreadNotifications, }: INotificationsPanelButtonComponentProps) { + const isDarkTheme = useIsDarkTheme(); return ( ); diff --git a/libs/sdk-ui-ext/src/notificationsPanel/NotificationsPanel/NotificationsPanel.tsx b/libs/sdk-ui-ext/src/notificationsPanel/NotificationsPanel/NotificationsPanel.tsx index c8efce35a6c..0eeb9a46843 100644 --- a/libs/sdk-ui-ext/src/notificationsPanel/NotificationsPanel/NotificationsPanel.tsx +++ b/libs/sdk-ui-ext/src/notificationsPanel/NotificationsPanel/NotificationsPanel.tsx @@ -256,7 +256,9 @@ function NotificationsPanelController({ const handleNotificationClick = useCallback( (notification: INotification) => { - markNotificationAsRead(notification.id); + if (!notification.isRead) { + markNotificationAsRead(notification.id); + } closeNotificationsPanel(); onNotificationClick?.(notification); }, diff --git a/libs/sdk-ui-ext/src/notificationsPanel/components/Popup.tsx b/libs/sdk-ui-ext/src/notificationsPanel/components/Popup.tsx new file mode 100644 index 00000000000..a34286945cc --- /dev/null +++ b/libs/sdk-ui-ext/src/notificationsPanel/components/Popup.tsx @@ -0,0 +1,36 @@ +// (C) 2024-2025 GoodData Corporation +import React, { useRef, useState, useCallback } from "react"; +import { Bubble } from "@gooddata/sdk-ui-kit"; +import { v4 as uuidv4 } from "uuid"; + +const ALIGN_POINTS = [{ align: "bc tr" }, { align: "tc br" }]; + +export function Popup({ + children, + popup, +}: { + children: ({ toggle, id }: { toggle: () => void; id: string }) => React.ReactNode; + popup: React.ReactNode; +}) { + const [isOpen, setIsOpen] = useState(false); + const { current: id } = useRef(`popup-${uuidv4()}`); + const toggle = useCallback(() => setIsOpen((x) => !x), []); + const close = useCallback(() => setIsOpen(false), []); + + return ( + <> + {children({ toggle, id })} + {isOpen ? ( + + {popup} + + ) : null} + + ); +} diff --git a/libs/sdk-ui-ext/src/notificationsPanel/data/useNotifications.tsx b/libs/sdk-ui-ext/src/notificationsPanel/data/useNotifications.tsx index 17f5c76ec6f..075a415fd59 100644 --- a/libs/sdk-ui-ext/src/notificationsPanel/data/useNotifications.tsx +++ b/libs/sdk-ui-ext/src/notificationsPanel/data/useNotifications.tsx @@ -10,7 +10,7 @@ import { useFetchNotifications } from "./useFetchNotifications.js"; /** * Hook for fetching all and unread notifications. * - * @public + * @internal */ export interface IUseNotificationsProps { /** @@ -38,7 +38,7 @@ export interface IUseNotificationsProps { } /** - * @public + * @internal */ export function useNotifications({ workspace, refreshInterval, itemsPerPage }: IUseNotificationsProps) { const effectiveWorkspace = useWorkspace(workspace); diff --git a/libs/sdk-ui-ext/src/notificationsPanel/notificationPanel.scss b/libs/sdk-ui-ext/src/notificationsPanel/notificationPanel.scss index 867465a35f0..ceac5ec9e54 100644 --- a/libs/sdk-ui-ext/src/notificationsPanel/notificationPanel.scss +++ b/libs/sdk-ui-ext/src/notificationsPanel/notificationPanel.scss @@ -1,4 +1,5 @@ // (C) 2024-2025 GoodData Corporation +@use "@gooddata/sdk-ui-kit/styles/scss/bubble.scss"; @use "@gooddata/sdk-ui-kit/styles/scss/overlay.scss"; @use "@gooddata/sdk-ui-kit/styles/scss/tabs.scss"; @use "@gooddata/sdk-ui-kit/src/@ui/UiIcon/UiIcon.scss"; diff --git a/libs/sdk-ui-kit/api/sdk-ui-kit.api.md b/libs/sdk-ui-kit/api/sdk-ui-kit.api.md index 729d22fa315..e371cd03f64 100644 --- a/libs/sdk-ui-kit/api/sdk-ui-kit.api.md +++ b/libs/sdk-ui-kit/api/sdk-ui-kit.api.md @@ -1225,7 +1225,7 @@ export interface IConfirmDialogBaseProps extends IDialogBaseProps { } // @internal (undocumented) -export type IconType = "check" | "plus" | "sync" | "alert" | "close" | "question"; +export type IconType = "check" | "plus" | "sync" | "alert" | "close" | "question" | "crossCircle"; // @internal (undocumented) export interface ICustomizableCheckmarkProps { diff --git a/libs/sdk-ui-kit/src/@ui/@types/icon.ts b/libs/sdk-ui-kit/src/@ui/@types/icon.ts index d614317dab5..8dc06dc2724 100644 --- a/libs/sdk-ui-kit/src/@ui/@types/icon.ts +++ b/libs/sdk-ui-kit/src/@ui/@types/icon.ts @@ -3,4 +3,4 @@ /** * @internal */ -export type IconType = "check" | "plus" | "sync" | "alert" | "close" | "question"; +export type IconType = "check" | "plus" | "sync" | "alert" | "close" | "question" | "crossCircle"; diff --git a/libs/sdk-ui-kit/src/@ui/UiIcon/icons.tsx b/libs/sdk-ui-kit/src/@ui/UiIcon/icons.tsx index fbef1a2f493..f9b76f9dc62 100644 --- a/libs/sdk-ui-kit/src/@ui/UiIcon/icons.tsx +++ b/libs/sdk-ui-kit/src/@ui/UiIcon/icons.tsx @@ -77,4 +77,13 @@ export const iconsConfig: Record = { ), viewBox: "0 0 20 20", }, + crossCircle: { + content: ( + + ), + viewBox: "0 0 12 12", + }, }; diff --git a/libs/sdk-ui-theme-provider/api/sdk-ui-theme-provider.api.md b/libs/sdk-ui-theme-provider/api/sdk-ui-theme-provider.api.md index e5fcebac686..768ba2d5b41 100644 --- a/libs/sdk-ui-theme-provider/api/sdk-ui-theme-provider.api.md +++ b/libs/sdk-ui-theme-provider/api/sdk-ui-theme-provider.api.md @@ -57,6 +57,9 @@ export const ThemeProvider: React_2.FC; // @public (undocumented) export type ThemeStatus = "pending" | "loading" | "success"; +// @public +export const useIsDarkTheme: () => boolean; + // @public export const useTheme: (theme?: ITheme) => ITheme | undefined; diff --git a/libs/sdk-ui-theme-provider/src/ThemeProvider/Context.tsx b/libs/sdk-ui-theme-provider/src/ThemeProvider/Context.tsx index 2757e6c2a19..b91f118983f 100644 --- a/libs/sdk-ui-theme-provider/src/ThemeProvider/Context.tsx +++ b/libs/sdk-ui-theme-provider/src/ThemeProvider/Context.tsx @@ -1,8 +1,9 @@ -// (C) 2019-2024 GoodData Corporation +// (C) 2019-2025 GoodData Corporation import React from "react"; import compose from "lodash/flowRight.js"; import { ITheme } from "@gooddata/sdk-model"; import { wrapDisplayName } from "@gooddata/sdk-ui"; +import { isDarkTheme } from "./isDarkTheme.js"; const ThemeContext = React.createContext(undefined); ThemeContext.displayName = "ThemeContext"; @@ -17,6 +18,7 @@ ThemeStatusContext.displayName = "ThemeStatusContext"; * @public */ export type ThemeStatus = "pending" | "loading" | "success"; + /** * @public */ @@ -91,6 +93,16 @@ export const useTheme = (theme?: ITheme): ITheme | undefined => { return theme ?? themeFromContext; }; +/** + * Hook that returns whether the current theme is dark + * + * @public + */ +export const useIsDarkTheme = (): boolean => { + const theme = useTheme(); + return isDarkTheme(theme); +}; + /** * Hook for reaching the themeIsLoading flag from context * diff --git a/libs/sdk-ui-theme-provider/src/ThemeProvider/ScopedThemeProvider.tsx b/libs/sdk-ui-theme-provider/src/ThemeProvider/ScopedThemeProvider.tsx index 50c332bad82..c65a75c1aa0 100644 --- a/libs/sdk-ui-theme-provider/src/ThemeProvider/ScopedThemeProvider.tsx +++ b/libs/sdk-ui-theme-provider/src/ThemeProvider/ScopedThemeProvider.tsx @@ -1,4 +1,4 @@ -// (C) 2020-2024 GoodData Corporation +// (C) 2020-2025 GoodData Corporation import React, { useEffect, useState, useRef } from "react"; import identity from "lodash/identity.js"; import { useBackend, useWorkspace } from "@gooddata/sdk-ui"; @@ -6,13 +6,10 @@ import { IAnalyticalBackend } from "@gooddata/sdk-backend-spi"; import { ITheme } from "@gooddata/sdk-model"; import { clearCssProperties, setCssProperties } from "../cssProperties.js"; -import { - ThemeContextProvider, - ThemeStatus, - // useTheme -} from "./Context.js"; +import { ThemeContextProvider, ThemeStatus } from "./Context.js"; import { prepareTheme } from "./prepareTheme.js"; -import { ThemeModifier, isDarkTheme } from "./ThemeProvider.js"; +import { ThemeModifier } from "./ThemeProvider.js"; +import { isDarkTheme } from "./isDarkTheme.js"; /** * @internal diff --git a/libs/sdk-ui-theme-provider/src/ThemeProvider/ThemeProvider.tsx b/libs/sdk-ui-theme-provider/src/ThemeProvider/ThemeProvider.tsx index aa1c9c0c5e0..a3974da43f7 100644 --- a/libs/sdk-ui-theme-provider/src/ThemeProvider/ThemeProvider.tsx +++ b/libs/sdk-ui-theme-provider/src/ThemeProvider/ThemeProvider.tsx @@ -1,6 +1,5 @@ -// (C) 2020-2024 GoodData Corporation +// (C) 2020-2025 GoodData Corporation import React, { useEffect, useState, useRef } from "react"; -import { getLuminance } from "polished"; import identity from "lodash/identity.js"; import { useBackend, useWorkspace } from "@gooddata/sdk-ui"; import { IAnalyticalBackend } from "@gooddata/sdk-backend-spi"; @@ -9,6 +8,7 @@ import { ITheme } from "@gooddata/sdk-model"; import { clearCssProperties, setCssProperties } from "../cssProperties.js"; import { ThemeContextProvider, ThemeStatus } from "./Context.js"; import { prepareTheme } from "./prepareTheme.js"; +import { isDarkTheme } from "./isDarkTheme.js"; /** * @public @@ -74,20 +74,6 @@ export interface IThemeProviderProps { children?: React.ReactNode; } -/** - * @internal - */ -export const isDarkTheme = (theme: ITheme): boolean => { - const firstColor = theme?.palette?.complementary?.c0; - const lastColor = theme?.palette?.complementary?.c9; - - if (!firstColor || !lastColor) { - return false; - } - - return getLuminance(firstColor) < getLuminance(lastColor); -}; - /** * Fetches the theme object from the backend upon mounting and passes both theme object and isThemeLoading flag * to the context via ThemeContextProvider. diff --git a/libs/sdk-ui-theme-provider/src/ThemeProvider/isDarkTheme.ts b/libs/sdk-ui-theme-provider/src/ThemeProvider/isDarkTheme.ts new file mode 100644 index 00000000000..4ef207a6f37 --- /dev/null +++ b/libs/sdk-ui-theme-provider/src/ThemeProvider/isDarkTheme.ts @@ -0,0 +1,17 @@ +// (C) 2025 GoodData Corporation +import { ITheme } from "@gooddata/sdk-model"; +import { getLuminance } from "polished"; + +/** + * @internal + */ +export const isDarkTheme = (theme: ITheme): boolean => { + const firstColor = theme?.palette?.complementary?.c0; + const lastColor = theme?.palette?.complementary?.c9; + + if (!firstColor || !lastColor) { + return false; + } + + return getLuminance(firstColor) < getLuminance(lastColor); +}; diff --git a/libs/sdk-ui-theme-provider/src/ThemeProvider/tests/ThemeProvider.test.tsx b/libs/sdk-ui-theme-provider/src/ThemeProvider/tests/ThemeProvider.test.tsx index acabae90ca5..752039a3359 100644 --- a/libs/sdk-ui-theme-provider/src/ThemeProvider/tests/ThemeProvider.test.tsx +++ b/libs/sdk-ui-theme-provider/src/ThemeProvider/tests/ThemeProvider.test.tsx @@ -1,4 +1,4 @@ -// (C) 2020-2024 GoodData Corporation +// (C) 2020-2025 GoodData Corporation import React from "react"; import { act } from "react-dom/test-utils"; import { render, RenderResult } from "@testing-library/react"; @@ -8,9 +8,10 @@ import { ITheme } from "@gooddata/sdk-model"; import { WorkspaceProvider, BackendProvider } from "@gooddata/sdk-ui"; import cloneDeep from "lodash/cloneDeep.js"; -import { isDarkTheme, ThemeModifier, ThemeProvider } from "../ThemeProvider.js"; +import { ThemeModifier, ThemeProvider } from "../ThemeProvider.js"; import { withTheme } from "../Context.js"; import { describe, it, expect, vi, beforeEach } from "vitest"; +import { isDarkTheme } from "../isDarkTheme.js"; const renderComponent = async (component: React.ReactElement): Promise => { let wrappedComponent; diff --git a/libs/sdk-ui-theme-provider/src/index.ts b/libs/sdk-ui-theme-provider/src/index.ts index 99485ab0af4..882df240c40 100644 --- a/libs/sdk-ui-theme-provider/src/index.ts +++ b/libs/sdk-ui-theme-provider/src/index.ts @@ -1,4 +1,4 @@ -// (C) 2020-2024 GoodData Corporation +// (C) 2020-2025 GoodData Corporation /** * This package provides tools to make your application support themes. * @@ -10,8 +10,9 @@ * @packageDocumentation */ export type { IThemeProviderProps, ThemeModifier } from "./ThemeProvider/ThemeProvider.js"; -export { ThemeProvider, isDarkTheme } from "./ThemeProvider/ThemeProvider.js"; +export { ThemeProvider } from "./ThemeProvider/ThemeProvider.js"; export { ScopedThemeProvider } from "./ThemeProvider/ScopedThemeProvider.js"; +export { isDarkTheme } from "./ThemeProvider/isDarkTheme.js"; export type { IScopedThemeProviderProps } from "./ThemeProvider/ScopedThemeProvider.js"; export type { IThemeContextProviderProps, ThemeStatus } from "./ThemeProvider/Context.js"; export { @@ -19,5 +20,6 @@ export { useTheme, useThemeIsLoading, useThemeStatus, + useIsDarkTheme, ThemeContextProvider, } from "./ThemeProvider/Context.js";