@polargold/pg-frontend-vue
    Preparing search index...

    @polargold/pg-frontend-vue

    pg-frontend-vue

    Vue 3 composable adapter for @polargold/pg-frontend-core — the shared, framework-agnostic utility layer.



    Beschreibung

    Prerequisites

    Getting started

    Useful commands

    Useful links

    Advanced topics



    Vue 3 composables built on top of @polargold/pg-frontend-core: reactive/lifecycle-aware wrappers (useApi, useLocalStorage, useDateTime, useCurrency, useColorGradient, useBreakpoint), router/permission composables (useKeyboardEvent, useRouterGo, useRouterPush, usePermission, useRoutePermission, useCheckTokenValidity), validation helpers (useValidationMethods for Vuelidate, useJoiValidation for Joi), DOM/accessibility composables (useFocusTrap, useClickOutside), and a few standalone ones (useLocalization, useLoadingState, useAppVersionCheck).

    Extracted from the near-duplicate composables found across luv-ui-tools, bws-ui-tools, and the luv-autoq-frontend/ama-deep/data-sheet-portal app repos during the September 2026 frontend tooling audit. Brand-specific composables (useSeminarType, useHomeLink, useSnackbarMethods/useSnackbarRetrieval) are not here — they live in each brand's own *-ui-library package instead. See What belongs here.


    • See NVM for installation.

    nvm use && npm install
    
    # Configure access to the polargold registry (once, globally). Token in Bitwarden under "[NPM Registry] pg-admin"
    npm config set -- //npm.white-lan.de/:_authToken=${token}
    cd your-project/

    echo "@polargold:registry=https://npm.white-lan.de" >> .npmrc
    npm install -S @polargold/pg-frontend-vue

    Peer dependencies: vue ^3.5. The rest are optional peer dependencies, only needed for the composable that uses them — install yourself if so, npm won't pull them in automatically:

    Peer dependency Needed for
    vue-router (^4 or ^5) useRouterGo, useRouterPush, useRoutePermission
    @vuelidate/core ^2.0.3 useValidationMethods
    i18next ^26.4.1 useLocalization

    joi is not a dependency of this package at all, despite useJoiValidation — it only appears as a TypeScript type (import type Joi from "joi"), which is erased at compile time. Verified in the built bundle: zero runtime reference. Bring your own joi install and construct the schema yourself; this composable just runs it.


    npm run lint
    npm run lint:fix
    npm run type-check
    npm run vitest:run
    npm run test
    npm run docs:build
    npm run docs:serve
    npm run vite:build
    

    Service URL
    Package registry Package registry
    TypeDoc pg-frontend-vue.polargold.dev

    Vue-specific composables only — anything using ref/computed/onMounted/useRouter/etc. Framework-agnostic logic (classes, plain functions, error types) belongs in @polargold/pg-frontend-core instead, which this package depends on and wraps. Brand-specific composables belong in that brand's *-ui-library package instead.

    That last rule cuts both ways: useFocusTrap/useClickOutside/useLocalization were found living in luv-ui-library/bws-ui-library (duplicated across both) despite having zero brand coupling — genuinely generic code sitting in the wrong package, the opposite mistake from useIconPosition (brand-specific-looking code that was actually too narrow to be generic). When re-scanning for anything missed, check *-ui-library composables, not just *-ui-tools — that's exactly where these three were hiding.

    Ported unchanged from luv-ui-tools/bws-ui-tools — reactive isLoading/apiData wrapper around an async callback, throwing ApiCallError (re-exported from pg-frontend-core) on failure.

    import { useApi } from "@polargold/pg-frontend-vue";

    const { isLoading, apiData, callApi } = useApi({
    callback: () => apiLibrary.myFeature.getItems(),
    onError: (error) => console.error(error),
    });

    Thin wrapper around pg-frontend-core's LocalStorage class.

    import { useLocalStorage } from "@polargold/pg-frontend-vue";

    const { write, read, remove } = useLocalStorage("accessToken", { mode: import.meta.env.MODE });

    Ported from luv-ui-tools/bws-ui-tools — thin wrappers around pg-frontend-core's DateFormatter/ CurrencyFormatter/ColorGradient.

    import { useDateTime, useCurrency, useColorGradient } from "@polargold/pg-frontend-vue";

    const { internationalDate } = useDateTime();
    const { transformCurrency } = useCurrency(); // or useCurrency("USD", "en-US")
    const { generateColorGradient } = useColorGradient();

    Ported from luv-ui-tools/bws-ui-tools — a window-width-driven set of breakpoint computeds.

    Not every brand/project uses the same responsive breakpoints, so the thresholds (BreakpointDefinitionValue: sm/md/lg/xl/2xl) are overridable — pass a partial object to change just the ones that differ from the defaults. Calling useBreakpoint() with no arguments behaves exactly as before.

    import { useBreakpoint } from "@polargold/pg-frontend-vue";

    const { isMdAndUp } = useBreakpoint(); // uses the shared defaults (sm: 480, md: 640, lg: 1024, xl: 1200, 2xl: 1440)

    // This brand's own Tailwind config uses different breakpoints:
    const { isMdAndUp: isMdAndUpCustom } = useBreakpoint({ sm: 320, md: 768, lg: 1024 });

    Replaces the copies in luv-autoq-frontend, ama-deep, bws-checkout-service-spa. Exposes raw addEventListener/removeEventListener rather than wiring its own onMounted/onUnmounted — wire it to whatever lifecycle fits your use case.

    import { onMounted, onUnmounted } from "vue";
    import { useKeyboardEvent } from "@polargold/pg-frontend-vue";

    const { addEventListener, removeEventListener } = useKeyboardEvent(() => close(), ["Escape"]);

    onMounted(addEventListener);
    onUnmounted(removeEventListener);

    Replaces the copies in luv-autoq-frontend, ama-deep, data-sheet-portal. The hardcoded console.group-logging + redirect-to-error-page behavior from the ama-deep/data-sheet-portal copies becomes an opt-in onError callback.

    import { useRouterGo } from "@polargold/pg-frontend-vue";
    import router from "@/router";
    import { ROUTE } from "@/router/routeDefinitions";

    const { routerGo } = useRouterGo({
    onError: async () => router.push({ name: ROUTE.PUBLIC_ERROR_PAGE.NAME }),
    });

    Replaces the copies in luv-autoq-frontend, ama-deep, data-sheet-portal (ama-deep/data-sheet-portal byte-identical; luv-autoq-frontend's also calls Sentry.captureException). Used internally by useRoutePermission for its own permission-redirects — see its onNavigationError option.

    import { useRouterPush } from "@polargold/pg-frontend-vue";
    import { ROUTE } from "@/router/routeDefinitions";

    const { routerPush } = useRouterPush({
    onError: async (error) => {
    console.error("Navigation failed", error);
    await router.push({ name: ROUTE.PUBLIC_PAGE_NOT_FOUND.NAME });
    },
    });

    Replaces the copies in luv-autoq-frontend, ama-deep, data-sheet-portal. Takes getUserRoles/isAdmin callbacks instead of importing an app-specific useAuthUserStore() directly.

    import { usePermission } from "@polargold/pg-frontend-vue";
    import { useAuthUserStore } from "@/stores/AppStartUp/AuthUserStore";

    const authUserStore = useAuthUserStore();
    const { hasAnyPermission, hasAllPermissions } = usePermission({
    getUserRoles: () => authUserStore.roles,
    isAdmin: () => authUserStore.isAdminUser,
    });

    Replaces the copies in ama-deep/data-sheet-portal (byte-identical between the two).

    Bug fix carried over: requires isAuthUserLoadedluv-autoq-frontend's copy of this composable was missing that guard entirely (found during the audit), so it isn't one of the source copies here.

    import { useRoutePermission } from "@polargold/pg-frontend-vue";
    import { useAuthUserStore } from "@/stores/AppStartUp/AuthUserStore";
    import { ROUTE } from "@/router/routeDefinitions";
    import router from "@/router";

    const authUserStore = useAuthUserStore();
    const { initialize } = useRoutePermission({
    getUserRoles: () => authUserStore.roles,
    isAdmin: () => authUserStore.isAdminUser,
    isAuthUserLoaded: () => authUserStore.authUserIsLoaded,
    notAllowedRouteName: ROUTE.PUBLIC_PAGE_NOT_ALLOWED.NAME,
    // optional - called if the permission-redirect itself fails (see useRouterPush above)
    onNavigationError: async (error) => router.push({ name: ROUTE.PUBLIC_ERROR_PAGE.NAME }),
    });

    await initialize();

    Route-level access is still declared the same way as before, via route.meta.hasAnyPermission / hasAllPermissions / excludeRoles / customRedirectName (this package augments Vue Router's RouteMeta type for these).

    Replaces the copies in ama-deep/data-sheet-portal (byte-identical between the two).

    import { useCheckTokenValidity } from "@polargold/pg-frontend-vue";
    import { useAccessTokenStore } from "@/stores/AppStartUp/AccessTokenStore";
    import { useOpenIdAuthenticationStore } from "@/stores/AppStartUp/OpenIdAuthenticationStore";

    const accessTokenStore = useAccessTokenStore();
    const authStore = useOpenIdAuthenticationStore();
    const { initialize, reset } = useCheckTokenValidity({
    isTokenValid: () => accessTokenStore.tokenIsValid,
    onInvalid: () => authStore.logout(),
    });

    Replaces a copy byte-identical between luv-autoq-frontend and bws-checkout-service-spa — found during a follow-up review pass, not in the original audit. Requires @vuelidate/core (optional peer dependency).

    import useVuelidate from "@vuelidate/core";
    import { required } from "@vuelidate/validators";
    import { useValidationMethods } from "@polargold/pg-frontend-vue";

    const state = reactive({ name: "" });
    const $v = useVuelidate({ name: { required } }, state);
    const { validate, hasErrorMessage, getErrorMessage } = useValidationMethods();

    const hasError = await validate($v); // resets, validates, returns $v.value.$error

    The Joi-flavored sibling to useValidationMethods — company convention (pg-api-guidelines-validation) names both Vuelidate (single forms) and Joi (assembled/aggregate data) as standard. Generalized from luv-autoq-frontend's useValidationStore: its app-specific field-name typing becomes a plain string, and its direct useI18n() import becomes a translate callback, so this package doesn't need a vue-i18n dependency. joi itself is not a runtime dependency of this package — see Peer dependencies above.

    import Joi from "joi";
    import { useI18n } from "vue-i18n";
    import { useJoiValidation } from "@polargold/pg-frontend-vue";

    const { t } = useI18n();
    const schema = Joi.object({ name: Joi.string().required() });
    const formData = reactive({ name: "" });

    const { validate, debouncedValidate, hasErrors, fieldHasError, getTranslatedErrorMessage } = useJoiValidation({
    validationSchema: schema,
    dataToValidate: () => formData,
    translate: t, // vue-i18n's t, or anything else - your choice
    });

    await validate();

    Replaces a copy byte-identical between luv-ui-library and bws-ui-library — found during a follow-up re-scan that checked the *-ui-library component packages too, not just *-ui-tools. Fully generic, so it belongs here instead. focusableSelector is an optional override if a container's focusable elements aren't covered by the sensible default list.

    import { useFocusTrap } from "@polargold/pg-frontend-vue";

    const modalRef = ref<HTMLElement>();
    const { activate, deactivate } = useFocusTrap(modalRef);

    Ported from luv-ui-library, found during the same re-scan. Fully generic. Structured like useFocusTrap (explicit activate/deactivate).

    import { useClickOutside } from "@polargold/pg-frontend-vue";

    const menuRef = ref<HTMLElement>();
    const { activate, deactivate } = useClickOutside([menuRef], () => closeMenu());

    Replaces a copy byte-identical between luv-ui-library and bws-ui-library — found during the same re-scan. Requires i18next (optional peer dependency).

    import { useLocalization } from "@polargold/pg-frontend-vue";

    const { translate } = useLocalization();

    translate("greeting", "Hello"); // falls back to "Hello" (or the key itself) if "greeting" doesn't exist

    Replaces the copy in luv-autoq-frontend. More general than useApi's built-in isLoading (tied to one callback) — useful for multi-step flows where the loading state doesn't map to a single async call.

    import { useLoadingState } from "@polargold/pg-frontend-vue";

    const { isLoading, activateLoadingState, deactivateLoadingState } = useLoadingState();

    Generalized from luv-autoq-frontend's copy — the standard "a new version is available, please reload" pattern (interval + tab-visibility + window-focus triggers). Its hardcoded /version.json fetch, direct Pinia store reads/writes, and direct @sentry/vue error reporting all become caller-supplied options.

    import { useAppVersionCheck } from "@polargold/pg-frontend-vue";
    import { useAppVersionStore } from "@/stores/appVersionStore";

    const appVersionStore = useAppVersionStore();
    const { initialize } = useAppVersionCheck({
    getCurrentVersion: async () => {
    try {
    const response = await fetch(`/version.json?t=${Date.now()}`, { cache: "no-store" });

    return (await response.json()).version;
    } catch (err) {
    Sentry.captureException(err);

    return undefined;
    }
    },
    getLoadedVersion: () => appVersionStore.loadedVersion,
    setLoadedVersion: (version) => appVersionStore.setLoadedVersion(version),
    isUpdateAlreadyFlagged: () => appVersionStore.updateAvailable,
    onUpdateAvailable: () => appVersionStore.flagUpdateAvailable(),
    });

    await initialize();

    Re-exported from @polargold/pg-frontend-core for convenience, so a Vue app consuming only this package doesn't need a separate pg-frontend-core import just for error handling.

    Every symbol is re-exported through src/composables/index.ts and through src/main.ts — nothing is auto-discovered. Follow this for anything new.

    Same as pg-frontend-core — everything pushed as a tag is type-checked, linted, tested, has its TypeDoc built, and is published to the internal package registry automatically. Versioning is manual:

    npm version [major | minor | patch | x.x.x]
    


    polargold GmbH, Lilienstraße 5-9 / Semperhaus C, 20095 Hamburg