Files
api/web/components/standalone-mount.ts
T
Eli Bosley 44774d0acd fix: UnraidToaster component and update dialog close button (#1657)
- Introduced a new UnraidToaster component for displaying notifications
with customizable positions.
- Updated the DialogClose component to use a span element for better
semantic structure.
- Enhanced CSS for the sonner component to ensure proper layout and
styling.

These changes improve user feedback through notifications and refine the
dialog close button's implementation.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a toaster notifications component with configurable screen
position, rich colors, and a close button; programmatic and legacy
mounting helpers exposed.

* **Style**
  * Updated toast close-button spacing and min-width behavior.
* Simplified dialog close-button rendering and removed redundant style
resets.
* Reduced SSO provider icon size and added SSO button font-size tokens.

* **Tests**
  * Added unit tests covering component mounting and global exports.

* **Chores**
  * Deployment now performs broader remote cleanup before syncing.
* **Chores**
* Type declarations and tsconfig updated for global mount/utility
typings.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-04 12:34:16 -04:00

131 lines
5.2 KiB
TypeScript

// Import all components
import Auth from './Auth.ce.vue';
import ConnectSettings from './ConnectSettings/ConnectSettings.ce.vue';
import DownloadApiLogs from './DownloadApiLogs.ce.vue';
import HeaderOsVersion from './HeaderOsVersion.ce.vue';
import Modals from './Modals.ce.vue';
import UserProfile from './UserProfile.ce.vue';
import UpdateOs from './UpdateOs.ce.vue';
import DowngradeOs from './DowngradeOs.ce.vue';
import Registration from './Registration.ce.vue';
import WanIpCheck from './WanIpCheck.ce.vue';
import WelcomeModal from './Activation/WelcomeModal.ce.vue';
import SsoButton from './SsoButton.ce.vue';
import LogViewer from './Logs/LogViewer.ce.vue';
import ThemeSwitcher from './ThemeSwitcher.ce.vue';
import ApiKeyPage from './ApiKeyPage.ce.vue';
import DevModalTest from './DevModalTest.ce.vue';
import ApiKeyAuthorize from './ApiKeyAuthorize.ce.vue';
import UnraidToaster from './UnraidToaster.vue';
// Import utilities
import { autoMountComponent, mountVueApp, getMountedApp } from './Wrapper/vue-mount-app';
import { useThemeStore } from '~/store/theme';
import { globalPinia } from '~/store/globalPinia';
import { client as apolloClient } from '~/helpers/create-apollo-client';
import { provideApolloClient } from '@vue/apollo-composable';
import { parse } from 'graphql';
import { ensureTeleportContainer } from '@unraid/ui';
// Window type definitions are automatically included via tsconfig.json
// Initialize global Apollo client context
if (typeof window !== 'undefined') {
// Make Apollo client globally available
window.apolloClient = apolloClient;
// Make graphql parse function available for browser console usage
window.graphqlParse = parse;
window.gql = parse;
// Provide Apollo client globally for all components
provideApolloClient(apolloClient);
// Initialize theme store and set CSS variables - this is needed by all components
const themeStore = useThemeStore(globalPinia);
themeStore.setTheme();
themeStore.setCssVars();
// Pre-create the teleport container to avoid mounting issues
// This ensures the container exists before any components try to teleport to it
ensureTeleportContainer();
}
// Define component mappings
const componentMappings = [
{ component: Auth, selector: 'unraid-auth', appId: 'auth' },
{ component: ConnectSettings, selector: 'unraid-connect-settings', appId: 'connect-settings' },
{ component: DownloadApiLogs, selector: 'unraid-download-api-logs', appId: 'download-api-logs' },
{ component: HeaderOsVersion, selector: 'unraid-header-os-version', appId: 'header-os-version' },
{ component: Modals, selector: 'unraid-modals', appId: 'modals' },
{ component: Modals, selector: '#modals', appId: 'modals-legacy' }, // Legacy ID selector
{ component: UserProfile, selector: 'unraid-user-profile', appId: 'user-profile' },
{ component: UpdateOs, selector: 'unraid-update-os', appId: 'update-os' },
{ component: DowngradeOs, selector: 'unraid-downgrade-os', appId: 'downgrade-os' },
{ component: Registration, selector: 'unraid-registration', appId: 'registration' },
{ component: WanIpCheck, selector: 'unraid-wan-ip-check', appId: 'wan-ip-check' },
{ component: WelcomeModal, selector: 'unraid-welcome-modal', appId: 'welcome-modal' },
{ component: SsoButton, selector: 'unraid-sso-button', appId: 'sso-button' },
{ component: LogViewer, selector: 'unraid-log-viewer', appId: 'log-viewer' },
{ component: ThemeSwitcher, selector: 'unraid-theme-switcher', appId: 'theme-switcher' },
{ component: ApiKeyPage, selector: 'unraid-api-key-manager', appId: 'api-key-manager' },
{ component: DevModalTest, selector: 'unraid-dev-modal-test', appId: 'dev-modal-test' },
{ component: ApiKeyAuthorize, selector: 'unraid-api-key-authorize', appId: 'api-key-authorize' },
{ component: UnraidToaster, selector: 'uui-toaster', appId: 'toaster' },
{ component: UnraidToaster, selector: 'unraid-toaster', appId: 'toaster-legacy' }, // Legacy alias
];
// Auto-mount all components
componentMappings.forEach(({ component, selector, appId }) => {
autoMountComponent(component, selector, {
appId,
useShadowRoot: false, // Mount directly to avoid shadow DOM issues
});
});
// Window interface extensions are defined in ~/types/window.d.ts
if (typeof window !== 'undefined') {
// Expose all components
window.UnraidComponents = {
Auth,
ConnectSettings,
DownloadApiLogs,
HeaderOsVersion,
Modals,
UserProfile,
UpdateOs,
DowngradeOs,
Registration,
WanIpCheck,
WelcomeModal,
SsoButton,
LogViewer,
ThemeSwitcher,
ApiKeyPage,
DevModalTest,
ApiKeyAuthorize,
UnraidToaster,
};
// Expose utility functions
window.mountVueApp = mountVueApp;
window.getMountedApp = getMountedApp;
// Create dynamic mount functions for each component
componentMappings.forEach(({ component, selector, appId }) => {
const componentName = appId.split('-').map(word =>
word.charAt(0).toUpperCase() + word.slice(1)
).join('');
(window as unknown as Record<string, unknown>)[`mount${componentName}`] = (customSelector?: string) => {
return mountVueApp({
component,
selector: customSelector || selector,
appId: `${appId}-${Date.now()}`,
useShadowRoot: false,
});
};
});
}