LCOV - code coverage report
Current view: top level - dashql-app/src/config - flavor_provider.tsx Coverage Total Hit
Test: DashQL 915828b Lines: 100.0 % 36 36
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : import symbols from '@ankoh/dashql-svg-symbols';
       3              : import { useLocation } from 'react-router-dom';
       4              : 
       5              : import { AppFlavor, DASHQL_FLAVOR } from './flavors.js';
       6              : import { applyFlavorToDocument, selectFlavor, withFlavorParameter } from './flavor_resolution.js';
       7              : 
       8              : interface FlavorState {
       9              :     /// The flavor of the app
      10              :     flavor: AppFlavor;
      11              :     /// The flavor requested by the url, null if the url requests none
      12              :     requested: AppFlavor | null;
      13              :     /// Select the flavor of the app settings, the flavor requested by the url still wins
      14              :     select: (id: string | undefined) => void;
      15              : }
      16              : 
      17            1 : const FLAVOR_CTX = React.createContext<FlavorState>({ flavor: DASHQL_FLAVOR, requested: null, select: () => { } });
      18              : 
      19              : type Props = {
      20              :     /// The flavor requested by the url, it wins over the flavor of the app settings
      21              :     requested: AppFlavor | null;
      22              :     /// The children
      23              :     children: React.ReactElement;
      24              : };
      25              : 
      26              : /// Provides the flavor of the app and brands the document with it
      27            1 : export const FlavorProvider: React.FC<Props> = (props: Props) => {
      28            1 :     const [settingId, setSettingId] = React.useState<string | undefined>(undefined);
      29            1 :     const flavor = selectFlavor(props.requested, settingId);
      30            1 :     React.useLayoutEffect(() => {
      31            1 :         applyFlavorToDocument(flavor);
      32            1 :     }, [flavor]);
      33            1 :     const state = React.useMemo<FlavorState>(() => ({
      34            1 :         flavor,
      35            1 :         requested: props.requested,
      36            1 :         select: setSettingId,
      37            1 :     }), [flavor, props.requested]);
      38            1 :     return <FLAVOR_CTX.Provider value={state}>{props.children}</FLAVOR_CTX.Provider>;
      39            1 : };
      40              : 
      41            1 : export const useAppFlavor = (): AppFlavor => React.useContext(FLAVOR_CTX).flavor;
      42            1 : export const useRequestedAppFlavor = (): AppFlavor | null => React.useContext(FLAVOR_CTX).requested;
      43            1 : export const useAppFlavorSelect = (): ((id: string | undefined) => void) => React.useContext(FLAVOR_CTX).select;
      44              : 
      45              : /// Keeps the flavor query parameter in the url when the router navigates, so that reloads keep the flavor.
      46              : /// With the hash router, the routes live in the hash and the query parameter is never dropped.
      47              : /// A flavor of the app settings is not added to the url, it would otherwise pin the flavor.
      48            1 : export function FlavorUrlKeeper() {
      49            1 :     const requested = useRequestedAppFlavor();
      50            1 :     const location = useLocation();
      51            1 :     React.useLayoutEffect(() => {
      52            1 :         const next = requested == null ? null : withFlavorParameter(new URL(globalThis.location.href), requested);
      53            1 :         if (next != null) {
      54              :             // Replace the url without the router, the history state still holds the router state
      55            1 :             globalThis.history.replaceState(globalThis.history.state, '', next);
      56              :         }
      57            1 :     }, [requested, location]);
      58            1 :     return null;
      59            1 : }
      60              : 
      61              : /// The logo of the app flavor, sized by either its height or its width
      62            1 : export function FlavorLogo(props: { height?: number; width?: number; ariaHidden?: boolean }) {
      63            1 :     const logo = useAppFlavor().logo;
      64            1 :     const height = props.height ?? (props.width ?? 0) / logo.aspectRatio;
      65            1 :     const width = props.width ?? height * logo.aspectRatio;
      66            1 :     return (
      67            1 :         <svg width={`${width}px`} height={`${height}px`} aria-hidden={props.ariaHidden}>
      68            1 :             <use xlinkHref={`${symbols}#${logo.symbol}`} />
      69            1 :         </svg>
      70              :     );
      71            1 : }
        

Generated by: LCOV version 2.0-1