1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-01-31 22:51:28 +01:00

[PM-17031] Create UI-common (#12831)

Extract core functionality from `libs/angular` to allow teams to depend on `libs/ui-common` instead.

Moves the following functionality to `ui-common`.
- `I18nPipe`. `libs/angular` still has an old copy but `components` depends on the new variant from `ui-common`.
- `safeProvider`, `SafeProvider` and `SafeInjectionToken`. `libs/angular`re-exports these to avoid needing to update all consumers.
This commit is contained in:
Oscar Hinton 2025-01-17 16:42:31 +01:00 committed by GitHub
parent 87171289f0
commit e5f83ff086
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
52 changed files with 300 additions and 247 deletions

1
.github/CODEOWNERS vendored
View File

@ -111,6 +111,7 @@ apps/desktop/desktop_native/core/src/ssh_agent @bitwarden/team-autofill-dev @bit
## Component Library ##
.storybook @bitwarden/team-design-system
libs/components @bitwarden/team-design-system
libs/ui @bitwarden/team-design-system
apps/browser/src/platform/popup/layout @bitwarden/team-design-system
apps/browser/src/popup/app-routing.animations.ts @bitwarden/team-design-system
apps/web/src/app/layouts @bitwarden/team-design-system

View File

@ -3,26 +3,12 @@
./apps/browser/src/safari/desktop/Assets.xcassets/AppIcon.appiconset
./apps/browser/src/safari/desktop/Base.lproj
./apps/browser/store/windows/Assets
./bitwarden_license/README.md
./libs/angular/src/directives/cipherListVirtualScroll.directive.ts
./libs/admin-console/README.md
./libs/auth/README.md
./libs/billing/README.md
./libs/common/src/tools/integration/README.md
./libs/platform/README.md
./libs/key-management/README.md
./libs/tools/README.md
./libs/tools/export/vault-export/README.md
./libs/tools/send/README.md
./libs/tools/card/README.md
./libs/vault/README.md
./README.md
./LICENSE_BITWARDEN.txt
./CONTRIBUTING.md
./LICENSE_GPL.txt
./LICENSE.txt
./apps/web/Dockerfile
./apps/web/README.md
./apps/desktop/resources/installerSidebar.bmp
./apps/desktop/resources/appx/SplashScreen.png
./apps/desktop/resources/appx/BadgeLogo.png
@ -30,10 +16,7 @@
./apps/desktop/resources/appx/StoreLogo.png
./apps/desktop/resources/appx/Wide310x150Logo.png
./apps/desktop/resources/appx/Square44x44Logo.png
./apps/desktop/README.md
./apps/cli/stores/chocolatey/tools/VERIFICATION.txt
./apps/cli/README.md
./apps/browser/README.md
./apps/browser/store/windows/AppxManifest.xml
./apps/browser/src/background/nativeMessaging.background.ts
./apps/browser/src/models/browserComponentState.ts

View File

@ -34,6 +34,7 @@ jobs:
! -path "*/.DS_Store" \
! -path "*/*locales/*" \
! -path "./.github/*" \
! -path "*/README.md" \
! -path "*/Cargo.toml" \
! -path "*/Cargo.lock" \
! -path "./apps/desktop/macos/*" \

View File

@ -27,9 +27,6 @@ import { FormFieldModule } from "../../../../../libs/components/src/form-field";
import { LinkModule } from "../../../../../libs/components/src/link";
// FIXME: remove `src` and fix import
// eslint-disable-next-line no-restricted-imports
import { I18nPipe } from "../../../../../libs/components/src/shared/i18n.pipe";
// FIXME: remove `src` and fix import
// eslint-disable-next-line no-restricted-imports
import { TypographyModule } from "../../../../../libs/components/src/typography";
import { ZonedMessageListenerService } from "../../platform/browser/zoned-message-listener.service";
@ -50,7 +47,6 @@ import { ZonedMessageListenerService } from "../../platform/browser/zoned-messag
AsyncActionsModule,
FormsModule,
],
providers: [I18nPipe],
})
export class TwoFactorAuthDuoComponent
extends TwoFactorAuthDuoBaseComponent

View File

@ -23,9 +23,6 @@ import { FormFieldModule } from "../../../../../libs/components/src/form-field";
import { LinkModule } from "../../../../../libs/components/src/link";
// FIXME: remove `src` and fix import
// eslint-disable-next-line no-restricted-imports
import { I18nPipe } from "../../../../../libs/components/src/shared/i18n.pipe";
// FIXME: remove `src` and fix import
// eslint-disable-next-line no-restricted-imports
import { TypographyModule } from "../../../../../libs/components/src/typography";
import BrowserPopupUtils from "../../platform/popup/browser-popup-utils";
@ -46,7 +43,6 @@ import BrowserPopupUtils from "../../platform/popup/browser-popup-utils";
AsyncActionsModule,
FormsModule,
],
providers: [I18nPipe],
})
export class TwoFactorAuthEmailComponent extends TwoFactorAuthEmailBaseComponent implements OnInit {
private dialogService = inject(DialogService);

View File

@ -24,17 +24,18 @@
"@bitwarden/generator-history": ["../../libs/tools/generator/extensions/history/src"],
"@bitwarden/generator-legacy": ["../../libs/tools/generator/extensions/legacy/src"],
"@bitwarden/generator-navigation": ["../../libs/tools/generator/extensions/navigation/src"],
"@bitwarden/importer/core": ["../../libs/importer/src"],
"@bitwarden/importer/ui": ["../../libs/importer/src/components"],
"@bitwarden/key-management": ["../../libs/key-management/src"],
"@bitwarden/key-management/angular": ["../../libs/key-management/src/angular"],
"@bitwarden/platform": ["../../libs/platform/src"],
"@bitwarden/send-ui": ["../../libs/tools/send/send-ui/src"],
"@bitwarden/tools-card": ["../../libs/tools/card/src"],
"@bitwarden/ui-common": ["../../libs/ui/common/src"],
"@bitwarden/vault-export-core": [
"../../libs/tools/export/vault-export/vault-export-core/src"
],
"@bitwarden/vault-export-ui": ["../../libs/tools/export/vault-export/vault-export-ui/src"],
"@bitwarden/importer/core": ["../../libs/importer/src"],
"@bitwarden/importer/ui": ["../../libs/importer/src/components"],
"@bitwarden/platform": ["../../libs/platform/src"],
"@bitwarden/send-ui": ["../../libs/tools/send/send-ui/src"],
"@bitwarden/tools-card": ["../../libs/tools/card/src"],
"@bitwarden/key-management": ["../../libs/key-management/src"],
"@bitwarden/key-management/angular": ["../../libs/key-management/src/angular"],
"@bitwarden/vault": ["../../libs/vault/src"]
},
"plugins": [

View File

@ -42,9 +42,6 @@ import { FormFieldModule } from "../../../../libs/components/src/form-field";
import { LinkModule } from "../../../../libs/components/src/link";
// FIXME: remove `src` and fix import
// eslint-disable-next-line no-restricted-imports
import { I18nPipe } from "../../../../libs/components/src/shared/i18n.pipe";
// FIXME: remove `src` and fix import
// eslint-disable-next-line no-restricted-imports
import { TypographyModule } from "../../../../libs/components/src/typography";
import { TwoFactorAuthDuoComponent } from "./two-factor-auth-duo.component";
@ -73,6 +70,5 @@ import { TwoFactorAuthDuoComponent } from "./two-factor-auth-duo.component";
TwoFactorAuthDuoComponent,
TwoFactorAuthWebAuthnComponent,
],
providers: [I18nPipe],
})
export class TwoFactorAuthComponent extends BaseTwoFactorAuthComponent {}

View File

@ -22,10 +22,6 @@
"@bitwarden/generator-history": ["../../libs/tools/generator/extensions/history/src"],
"@bitwarden/generator-legacy": ["../../libs/tools/generator/extensions/legacy/src"],
"@bitwarden/generator-navigation": ["../../libs/tools/generator/extensions/navigation/src"],
"@bitwarden/vault-export-core": [
"../../libs/tools/export/vault-export/vault-export-core/src"
],
"@bitwarden/vault-export-ui": ["../../libs/tools/export/vault-export/vault-export-ui/src"],
"@bitwarden/importer/core": ["../../libs/importer/src"],
"@bitwarden/importer/ui": ["../../libs/importer/src/components"],
"@bitwarden/key-management": ["../../libs/key-management/src"],
@ -34,6 +30,11 @@
"@bitwarden/platform": ["../../libs/platform/src"],
"@bitwarden/send-ui": ["../../libs/tools/send/send-ui/src"],
"@bitwarden/tools-card": ["../../libs/tools/card/src"],
"@bitwarden/ui-common": ["../../libs/ui/common/src"],
"@bitwarden/vault-export-core": [
"../../libs/tools/export/vault-export/vault-export-core/src"
],
"@bitwarden/vault-export-ui": ["../../libs/tools/export/vault-export/vault-export-ui/src"],
"@bitwarden/vault": ["../../libs/vault/src"]
},
"plugins": [

View File

@ -7,7 +7,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { ThemeType } from "@bitwarden/common/platform/enums";
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
import { SharedModule } from "@bitwarden/components/src/shared";
import { I18nPipe } from "@bitwarden/components/src/shared/i18n.pipe";
import { I18nPipe } from "@bitwarden/ui-common";
import { IntegrationCardComponent } from "./integration-card.component";

View File

@ -9,7 +9,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
import { ThemeTypes } from "@bitwarden/common/platform/enums";
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
import { SharedModule } from "@bitwarden/components/src/shared";
import { I18nPipe } from "@bitwarden/components/src/shared/i18n.pipe";
import { I18nPipe } from "@bitwarden/ui-common";
import { IntegrationCardComponent } from "../integration-card/integration-card.component";
import { Integration } from "../models";

View File

@ -7,8 +7,8 @@
"paths": {
"@bitwarden/admin-console/common": ["../../libs/admin-console/src/common"],
"@bitwarden/angular/*": ["../../libs/angular/src/*"],
"@bitwarden/auth/common": ["../../libs/auth/src/common"],
"@bitwarden/auth/angular": ["../../libs/auth/src/angular"],
"@bitwarden/auth/common": ["../../libs/auth/src/common"],
"@bitwarden/billing": ["../../libs/billing/src"],
"@bitwarden/bit-common/*": ["../../bitwarden_license/bit-common/src/*"],
"@bitwarden/common/*": ["../../libs/common/src/*"],
@ -18,10 +18,6 @@
"@bitwarden/generator-history": ["../../libs/tools/generator/extensions/history/src"],
"@bitwarden/generator-legacy": ["../../libs/tools/generator/extensions/legacy/src"],
"@bitwarden/generator-navigation": ["../../libs/tools/generator/extensions/navigation/src"],
"@bitwarden/vault-export-core": [
"../../libs/tools/export/vault-export/vault-export-core/src"
],
"@bitwarden/vault-export-ui": ["../../libs/tools/export/vault-export/vault-export-ui/src"],
"@bitwarden/importer/core": ["../../libs/importer/src"],
"@bitwarden/importer/ui": ["../../libs/importer/src/components"],
"@bitwarden/key-management": ["../../libs/key-management/src"],
@ -29,6 +25,11 @@
"@bitwarden/platform": ["../../libs/platform/src"],
"@bitwarden/send-ui": ["../../libs/tools/send/send-ui/src"],
"@bitwarden/tools-card": ["../../libs/tools/card/src"],
"@bitwarden/ui-common": ["../../libs/ui/common/src"],
"@bitwarden/vault-export-core": [
"../../libs/tools/export/vault-export/vault-export-core/src"
],
"@bitwarden/vault-export-ui": ["../../libs/tools/export/vault-export/vault-export-ui/src"],
"@bitwarden/vault": ["../../libs/vault/src"],
"@bitwarden/web-vault/*": ["src/*"]
}

View File

@ -9,6 +9,7 @@
"@bitwarden/angular/*": ["../../libs/angular/src/*"],
"@bitwarden/auth": ["../../libs/auth/src"],
"@bitwarden/billing": ["../../libs/billing/src"],
"@bitwarden/bit-common/*": ["../bit-common/src/*"],
"@bitwarden/common/*": ["../../libs/common/src/*"],
"@bitwarden/components": ["../../libs/components/src"],
"@bitwarden/generator-components": ["../../libs/tools/generator/components/src"],
@ -16,18 +17,18 @@
"@bitwarden/generator-history": ["../../libs/tools/generator/extensions/history/src"],
"@bitwarden/generator-legacy": ["../../libs/tools/generator/extensions/legacy/src"],
"@bitwarden/generator-navigation": ["../../libs/tools/generator/extensions/navigation/src"],
"@bitwarden/key-management": ["../../libs/key-management/src"],
"@bitwarden/key-management/angular": ["../../libs/key-management/src/angular"],
"@bitwarden/platform": ["../../libs/platform/src"],
"@bitwarden/send-ui": ["../../libs/tools/send/send-ui/src"],
"@bitwarden/tools-card": ["../../libs/tools/card/src"],
"@bitwarden/ui-common": ["../../libs/ui/common/src"],
"@bitwarden/vault-export-core": [
"../../libs/tools/export/vault-export/vault-export-core/src"
],
"@bitwarden/vault-export-ui": ["../../libs/tools/export/vault-export/vault-export-core/src"],
"@bitwarden/send-ui": ["../../libs/tools/send/send-ui/src"],
"@bitwarden/tools-card": ["../../libs/tools/card/src"],
"@bitwarden/key-management": ["../../libs/key-management/src"],
"@bitwarden/key-management/angular": ["../../libs/key-management/src/angular"],
"@bitwarden/platform": ["../../libs/platform/src"],
"@bitwarden/vault": ["../../libs/vault/src"],
"@bitwarden/web-vault/*": ["../../apps/web/src/*"],
"@bitwarden/bit-common/*": ["../bit-common/src/*"]
"@bitwarden/web-vault/*": ["../../apps/web/src/*"]
}
}
}

View File

@ -26,6 +26,7 @@
"@bitwarden/key-management": ["../../libs/key-management/src"],
"@bitwarden/key-management/angular": ["../../libs/key-management/src/angular"],
"@bitwarden/platform": ["../../libs/platform/src"],
"@bitwarden/ui-common": ["../../libs/ui/common/src"],
"@bitwarden/send-ui": ["../../libs/tools/send/send-ui/src"],
"@bitwarden/tools-card": ["../../libs/tools/card/src"],
"@bitwarden/vault": ["../../libs/vault/src"],

View File

@ -2,6 +2,9 @@ import { Pipe, PipeTransform } from "@angular/core";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
/**
* @deprecated: Please use the I18nPipe from @bitwarden/ui-common
*/
@Pipe({
name: "i18n",
})

View File

@ -1,138 +1,4 @@
import { Provider } from "@angular/core";
import { Constructor, Opaque } from "type-fest";
import { SafeInjectionToken } from "../../services/injection-tokens";
/**
* The return type of the {@link safeProvider} helper function.
* Used to distinguish a type safe provider definition from a non-type safe provider definition.
* @deprecated: Please use the SafeProvider & safeProvider from @bitwarden/ui-common
*/
export type SafeProvider = Opaque<Provider>;
// TODO: type-fest also provides a type like this when we upgrade >= 3.7.0
type AbstractConstructor<T> = abstract new (...args: any) => T;
type MapParametersToDeps<T> = {
[K in keyof T]: AbstractConstructor<T[K]> | SafeInjectionToken<T[K]>;
};
type SafeInjectionTokenType<T> = T extends SafeInjectionToken<infer J> ? J : never;
/**
* Gets the instance type from a constructor, abstract constructor, or SafeInjectionToken
*/
type ProviderInstanceType<T> =
T extends SafeInjectionToken<any>
? InstanceType<SafeInjectionTokenType<T>>
: T extends Constructor<any> | AbstractConstructor<any>
? InstanceType<T>
: never;
/**
* Represents a dependency provided with the useClass option.
*/
type SafeClassProvider<
A extends AbstractConstructor<any> | SafeInjectionToken<any>,
I extends Constructor<ProviderInstanceType<A>>,
D extends MapParametersToDeps<ConstructorParameters<I>>,
> = {
provide: A;
useClass: I;
deps: D;
};
/**
* Represents a dependency provided with the useValue option.
*/
type SafeValueProvider<A extends SafeInjectionToken<any>, V extends SafeInjectionTokenType<A>> = {
provide: A;
useValue: V;
};
/**
* Represents a dependency provided with the useFactory option.
*/
type SafeFactoryProvider<
A extends AbstractConstructor<any> | SafeInjectionToken<any>,
I extends (...args: any) => ProviderInstanceType<A>,
D extends MapParametersToDeps<Parameters<I>>,
> = {
provide: A;
useFactory: I;
deps: D;
multi?: boolean;
};
/**
* Represents a dependency provided with the useExisting option.
*/
type SafeExistingProvider<
A extends Constructor<any> | AbstractConstructor<any> | SafeInjectionToken<any>,
I extends Constructor<ProviderInstanceType<A>> | AbstractConstructor<ProviderInstanceType<A>>,
> = {
provide: A;
useExisting: I;
};
/**
* Represents a dependency where there is no abstract token, the token is the implementation
*/
type SafeConcreteProvider<
I extends Constructor<any>,
D extends MapParametersToDeps<ConstructorParameters<I>>,
> = {
provide: I;
deps: D;
};
/**
* If useAngularDecorators: true is specified, do not require a deps array.
* This is a manual override for where @Injectable decorators are used
*/
type UseAngularDecorators<T extends { deps: any }> = Omit<T, "deps"> & {
useAngularDecorators: true;
};
/**
* Represents a type with a deps array that may optionally be overridden with useAngularDecorators
*/
type AllowAngularDecorators<T extends { deps: any }> = T | UseAngularDecorators<T>;
/**
* A factory function that creates a provider for the ngModule providers array.
* This (almost) guarantees type safety for your provider definition. It does nothing at runtime.
* Warning: the useAngularDecorators option provides an override where your class uses the Injectable decorator,
* however this cannot be enforced by the type system and will not cause an error if the decorator is not used.
* @example safeProvider({ provide: MyService, useClass: DefaultMyService, deps: [AnotherService] })
* @param provider Your provider object in the usual shape (e.g. using useClass, useValue, useFactory, etc.)
* @returns The exact same object without modification (pass-through).
*/
export const safeProvider = <
// types for useClass
AClass extends AbstractConstructor<any> | SafeInjectionToken<any>,
IClass extends Constructor<ProviderInstanceType<AClass>>,
DClass extends MapParametersToDeps<ConstructorParameters<IClass>>,
// types for useValue
AValue extends SafeInjectionToken<any>,
VValue extends SafeInjectionTokenType<AValue>,
// types for useFactory
AFactory extends AbstractConstructor<any> | SafeInjectionToken<any>,
IFactory extends (...args: any) => ProviderInstanceType<AFactory>,
DFactory extends MapParametersToDeps<Parameters<IFactory>>,
// types for useExisting
AExisting extends Constructor<any> | AbstractConstructor<any> | SafeInjectionToken<any>,
IExisting extends
| Constructor<ProviderInstanceType<AExisting>>
| AbstractConstructor<ProviderInstanceType<AExisting>>,
// types for no token
IConcrete extends Constructor<any>,
DConcrete extends MapParametersToDeps<ConstructorParameters<IConcrete>>,
>(
provider:
| AllowAngularDecorators<SafeClassProvider<AClass, IClass, DClass>>
| SafeValueProvider<AValue, VValue>
| AllowAngularDecorators<SafeFactoryProvider<AFactory, IFactory, DFactory>>
| SafeExistingProvider<AExisting, IExisting>
| AllowAngularDecorators<SafeConcreteProvider<IConcrete, DConcrete>>
| Constructor<unknown>,
): SafeProvider => provider as SafeProvider;
export { SafeProvider, safeProvider } from "@bitwarden/ui-common";

View File

@ -1,6 +1,5 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { InjectionToken } from "@angular/core";
import { Observable, Subject } from "rxjs";
import { LogoutReason } from "@bitwarden/auth/common";
@ -14,17 +13,9 @@ import { Theme } from "@bitwarden/common/platform/enums";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
import { Message } from "@bitwarden/common/platform/messaging";
import { VaultTimeout } from "@bitwarden/common/types/vault-timeout.type";
declare const tag: unique symbol;
/**
* A (more) typesafe version of InjectionToken which will more strictly enforce the generic type parameter.
* @remarks The default angular implementation does not use the generic type to define the structure of the object,
* so the structural type system will not complain about a mismatch in the type parameter.
* This is solved by assigning T to an arbitrary private property.
*/
export class SafeInjectionToken<T> extends InjectionToken<T> {
private readonly [tag]: T;
}
import { SafeInjectionToken } from "@bitwarden/ui-common";
// Re-export the SafeInjectionToken from ui-common
export { SafeInjectionToken } from "@bitwarden/ui-common";
export const WINDOW = new SafeInjectionToken<Window>("WINDOW");
export const OBSERVABLE_MEMORY_STORAGE = new SafeInjectionToken<

View File

@ -293,6 +293,7 @@ import {
UserAsymmetricKeysRegenerationApiService,
DefaultUserAsymmetricKeysRegenerationApiService,
} from "@bitwarden/key-management";
import { SafeInjectionToken } from "@bitwarden/ui-common";
import { PasswordRepromptService } from "@bitwarden/vault";
import {
VaultExportService,
@ -323,7 +324,6 @@ import {
MEMORY_STORAGE,
OBSERVABLE_DISK_STORAGE,
OBSERVABLE_MEMORY_STORAGE,
SafeInjectionToken,
SECURE_STORAGE,
STATE_FACTORY,
SUPPORTS_SECURE_STORAGE,

View File

@ -16,6 +16,7 @@
"@bitwarden/importer/core": ["../importer/src"],
"@bitwarden/key-management": ["../key-management/src"],
"@bitwarden/platform": ["../platform/src"],
"@bitwarden/ui-common": ["../ui/common/src"],
"@bitwarden/vault-export-core": ["../tools/export/vault-export/vault-export-core/src"],
"@bitwarden/vault": ["../vault/src"]
}

View File

@ -4,17 +4,18 @@
"resolveJsonModule": true,
"paths": {
"@bitwarden/admin-console/common": ["../admin-console/src/common"],
"@bitwarden/auth/common": ["../auth/src/common"],
"@bitwarden/auth/angular": ["../auth/src/angular"],
"@bitwarden/angular/*": ["../angular/src/*"],
"@bitwarden/auth/angular": ["../auth/src/angular"],
"@bitwarden/auth/common": ["../auth/src/common"],
"@bitwarden/common/*": ["../common/src/*"],
"@bitwarden/components": ["../components/src"],
"@bitwarden/key-management": ["../key-management/src"],
"@bitwarden/platform": ["../platform/src"],
"@bitwarden/generator-core": ["../tools/generator/core/src"],
"@bitwarden/generator-history": ["../tools/generator/extensions/history/src"],
"@bitwarden/generator-legacy": ["../tools/generator/extensions/legacy/src"],
"@bitwarden/generator-navigation": ["../tools/generator/extensions/navigation/src"]
"@bitwarden/generator-navigation": ["../tools/generator/extensions/navigation/src"],
"@bitwarden/key-management": ["../key-management/src"],
"@bitwarden/platform": ["../platform/src"],
"@bitwarden/ui-common": ["../ui/common/src"]
}
},
"include": ["src", "spec"],

View File

@ -6,9 +6,12 @@
"@bitwarden/auth/common": ["../auth/src/common"],
// TODO: Remove once circular dependencies in admin-console, auth and key-management are resolved
"@bitwarden/common/*": ["../common/src/*"],
// TODO: Remove once billing stops depending on components
"@bitwarden/components": ["../components/src"],
"@bitwarden/key-management": ["../key-management/src"],
"@bitwarden/platform": ["../platform/src"]
"@bitwarden/platform": ["../platform/src"],
// TODO: Remove once billing stops depending on components
"@bitwarden/ui-common": ["../ui/common/src"]
}
},
"include": ["src", "spec", "./custom-matchers.d.ts", "../key-management/src/index.ts"],

View File

@ -3,8 +3,9 @@
import { CommonModule } from "@angular/common";
import { Component, Input, OnChanges } from "@angular/core";
import { I18nPipe } from "@bitwarden/ui-common";
import { BadgeModule, BadgeVariant } from "../badge";
import { I18nPipe } from "../shared/i18n.pipe";
@Component({
selector: "bit-badge-list",

View File

@ -3,8 +3,9 @@
import { CommonModule } from "@angular/common";
import { Component, Input, OnInit, Output, EventEmitter } from "@angular/core";
import { I18nPipe } from "@bitwarden/ui-common";
import { IconButtonModule } from "../icon-button";
import { I18nPipe } from "../shared/i18n.pipe";
type BannerTypes = "premium" | "info" | "warning" | "danger";

View File

@ -4,8 +4,9 @@ import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { CommonModule } from "@angular/common";
import { Component, HostBinding, Input } from "@angular/core";
import { I18nPipe } from "@bitwarden/ui-common";
import { BitIconButtonComponent } from "../../icon-button/icon-button.component";
import { I18nPipe } from "../../shared/i18n.pipe";
import { TypographyDirective } from "../../typography/typography.directive";
import { fadeIn } from "../animations";
import { DialogCloseDirective } from "../directives/dialog-close.directive";

View File

@ -5,8 +5,8 @@ import { NgClass, NgIf } from "@angular/common";
import { Component, ContentChild, HostBinding, Input } from "@angular/core";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { I18nPipe } from "@bitwarden/ui-common";
import { I18nPipe } from "../shared/i18n.pipe";
import { TypographyDirective } from "../typography/typography.directive";
import { BitFormControlAbstraction } from "./form-control.abstraction";

View File

@ -4,7 +4,7 @@ import { NgIf } from "@angular/common";
import { Component, Input } from "@angular/core";
import { AbstractControl, UntypedFormGroup } from "@angular/forms";
import { I18nPipe } from "../shared/i18n.pipe";
import { I18nPipe } from "@bitwarden/ui-common";
@Component({
selector: "bit-error-summary",

View File

@ -14,10 +14,11 @@ import {
signal,
} from "@angular/core";
import { I18nPipe } from "@bitwarden/ui-common";
import { BitHintComponent } from "../form-control/hint.component";
import { BitLabel } from "../form-control/label.component";
import { inputBorderClasses } from "../input/input.directive";
import { I18nPipe } from "../shared/i18n.pipe";
import { BitErrorComponent } from "./error.component";
import { BitFormFieldControl } from "./form-field-control";

View File

@ -24,10 +24,10 @@ import {
import { NgSelectComponent, NgSelectModule } from "@ng-select/ng-select";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { I18nPipe } from "@bitwarden/ui-common";
import { BadgeModule } from "../badge";
import { BitFormFieldControl } from "../form-field/form-field-control";
import { I18nPipe } from "../shared/i18n.pipe";
import { SelectItemView } from "./models/select-item-view";

View File

@ -12,8 +12,9 @@ import {
SkipSelf,
} from "@angular/core";
import { I18nPipe } from "@bitwarden/ui-common";
import { IconButtonModule } from "../icon-button";
import { I18nPipe } from "../shared/i18n.pipe";
import { NavBaseComponent } from "./nav-base.component";
import { NavGroupAbstraction, NavItemComponent } from "./nav-item.component";

View File

@ -4,8 +4,9 @@ import { CdkTrapFocus } from "@angular/cdk/a11y";
import { CommonModule } from "@angular/common";
import { Component, ElementRef, Input, ViewChild } from "@angular/core";
import { I18nPipe } from "@bitwarden/ui-common";
import { BitIconButtonComponent } from "../icon-button/icon-button.component";
import { I18nPipe } from "../shared/i18n.pipe";
import { NavDividerComponent } from "./nav-divider.component";
import { SideNavService } from "./side-nav.service";

View File

@ -4,8 +4,9 @@ import { NgIf, NgTemplateOutlet } from "@angular/common";
import { Component, ContentChild, HostBinding, Input, Optional, Self } from "@angular/core";
import { ControlValueAccessor, NgControl, Validators } from "@angular/forms";
import { I18nPipe } from "@bitwarden/ui-common";
import { BitLabel } from "../form-control/label.component";
import { I18nPipe } from "../shared/i18n.pipe";
let nextId = 0;

View File

@ -9,10 +9,10 @@ import {
} from "@angular/forms";
import { isBrowserSafariApi } from "@bitwarden/platform";
import { I18nPipe } from "@bitwarden/ui-common";
import { InputModule } from "../input/input.module";
import { FocusableElement } from "../shared/focusable-element";
import { I18nPipe } from "../shared/i18n.pipe";
let nextId = 0;

View File

@ -1,7 +1,7 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { I18nPipe } from "./i18n.pipe";
import { I18nPipe } from "@bitwarden/ui-common";
@NgModule({
imports: [CommonModule, I18nPipe],

View File

@ -20,7 +20,8 @@
"lib": ["es2020", "dom"],
"paths": {
"@bitwarden/common/*": ["../common/src/*"],
"@bitwarden/platform": ["../platform/src"]
"@bitwarden/platform": ["../platform/src"],
"@bitwarden/ui-common": ["../ui/common/src"]
},
"plugins": [
{

View File

@ -13,6 +13,7 @@
"@bitwarden/generator-navigation": ["../tools/generator/extensions/navigation/src"],
"@bitwarden/key-management": ["../key-management/src"],
"@bitwarden/platform": ["../platform/src"],
"@bitwarden/ui-common": ["../ui/common/src"],
"@bitwarden/vault-export-core": ["../tools/export/vault-export/vault-export-core/src"]
}
},

View File

@ -13,7 +13,8 @@
"@bitwarden/generator-history": ["../tools/generator/extensions/history/src"],
"@bitwarden/generator-legacy": ["../tools/generator/extensions/legacy/src"],
"@bitwarden/generator-navigation": ["../tools/generator/extensions/navigation/src"],
"@bitwarden/platform": ["../platform/src"]
"@bitwarden/platform": ["../platform/src"],
"@bitwarden/ui-common": ["../ui/common/src"]
}
},
"include": ["src", "spec"],

View File

@ -5,8 +5,8 @@
"paths": {
"@bitwarden/admin-console/common": ["../admin-console/src/common"],
"@bitwarden/angular/*": ["../angular/src/*"],
"@bitwarden/auth/common": ["../auth/src/common"],
"@bitwarden/auth/angular": ["../auth/src/angular"],
"@bitwarden/auth/common": ["../auth/src/common"],
"@bitwarden/billing": ["../billing/src"],
"@bitwarden/common/*": ["../common/src/*"],
"@bitwarden/components": ["../components/src"],
@ -15,16 +15,17 @@
"@bitwarden/generator-history": ["../tools/generator/extensions/history/src"],
"@bitwarden/generator-legacy": ["../tools/generator/extensions/legacy/src"],
"@bitwarden/generator-navigation": ["../tools/generator/extensions/navigation/src"],
"@bitwarden/vault-export-core": ["../tools/export/vault-export/vault-export-core/src"],
"@bitwarden/vault-export-ui": ["../tools/export/vault-export/vault-export-ui/src"],
"@bitwarden/importer/core": ["../importer/src"],
"@bitwarden/importer/ui": ["../importer/src/components"],
"@bitwarden/key-management": ["../key-management/src"],
"@bitwarden/key-management/angular": ["../key-management/src/angular"],
"@bitwarden/node/*": ["../node/src/*"],
"@bitwarden/platform": ["../platform/src"],
"@bitwarden/send-ui": ["../tools/send/send-ui/src"],
"@bitwarden/tools-card": ["../tools/card/src"],
"@bitwarden/node/*": ["../node/src/*"],
"@bitwarden/ui-common": ["../ui/common/src"],
"@bitwarden/vault-export-core": ["../tools/export/vault-export/vault-export-core/src"],
"@bitwarden/vault-export-ui": ["../tools/export/vault-export/vault-export-ui/src"],
"@bitwarden/vault": ["../vault/src"]
}
}

View File

@ -8,7 +8,8 @@
"@bitwarden/common/*": ["../../common/src/*"],
"@bitwarden/components": ["../../components/src"],
"@bitwarden/key-management": ["../../key-management/src"],
"@bitwarden/platform": ["../../platform/src"]
"@bitwarden/platform": ["../../platform/src"],
"@bitwarden/ui-common": ["../../ui/common/src"]
}
},
"include": ["src"],

View File

@ -14,6 +14,7 @@
"@bitwarden/generator-navigation": ["../../../../tools/generator/extensions/navigation/src"],
"@bitwarden/key-management": ["../../../../key-management/src"],
"@bitwarden/platform": ["../../../../platform/src"],
"@bitwarden/ui-common": ["../../../../ui/common/src"],
"@bitwarden/vault-export-core": [
"../../../../tools/export/vault-export/vault-export-core/src"
]

View File

@ -10,7 +10,8 @@
"@bitwarden/generator-core": ["../../../tools/generator/core/src"],
"@bitwarden/generator-history": ["../../../tools/generator/extensions/history/src"],
"@bitwarden/key-management": ["../../../key-management/src"],
"@bitwarden/platform": ["../../../platform/src"]
"@bitwarden/platform": ["../../../platform/src"],
"@bitwarden/ui-common": ["../../../ui/common/src"]
}
},
"include": ["src"],

View File

@ -13,7 +13,8 @@
"@bitwarden/generator-legacy": ["../../../tools/generator/extensions/legacy/src"],
"@bitwarden/generator-navigation": ["../../../tools/generator/extensions/navigation/src"],
"@bitwarden/key-management": ["../../../key-management/src"],
"@bitwarden/platform": ["../../../platform/src"]
"@bitwarden/platform": ["../../../platform/src"],
"@bitwarden/ui-common": ["../../../ui/common/src"]
}
},
"include": ["src"],

5
libs/ui/README.md Normal file
View File

@ -0,0 +1,5 @@
# UI Foundation
Core UI libraries maintained by the UI Foundation team.
- _ui-common_: Low-level utilities for Angular applications.

View File

@ -0,0 +1,15 @@
{
"name": "@bitwarden/ui-common",
"version": "0.0.0",
"description": "Low-level utilities for Angular applications",
"keywords": [
"bitwarden"
],
"author": "Bitwarden Inc.",
"homepage": "https://bitwarden.com",
"repository": {
"type": "git",
"url": "https://github.com/bitwarden/clients"
},
"license": "GPL-3.0"
}

View File

@ -0,0 +1,2 @@
export * from "./safe-injection-token";
export * from "./safe-provider";

View File

@ -0,0 +1,14 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { InjectionToken } from "@angular/core";
declare const tag: unique symbol;
/**
* A (more) typesafe version of InjectionToken which will more strictly enforce the generic type parameter.
* @remarks The default angular implementation does not use the generic type to define the structure of the object,
* so the structural type system will not complain about a mismatch in the type parameter.
* This is solved by assigning T to an arbitrary private property.
*/
export class SafeInjectionToken<T> extends InjectionToken<T> {
private readonly [tag]: T;
}

View File

@ -0,0 +1,138 @@
import { Provider } from "@angular/core";
import { Constructor, Opaque } from "type-fest";
import { SafeInjectionToken } from "./safe-injection-token";
/**
* The return type of the {@link safeProvider} helper function.
* Used to distinguish a type safe provider definition from a non-type safe provider definition.
*/
export type SafeProvider = Opaque<Provider>;
// TODO: type-fest also provides a type like this when we upgrade >= 3.7.0
type AbstractConstructor<T> = abstract new (...args: any) => T;
type MapParametersToDeps<T> = {
[K in keyof T]: AbstractConstructor<T[K]> | SafeInjectionToken<T[K]>;
};
type SafeInjectionTokenType<T> = T extends SafeInjectionToken<infer J> ? J : never;
/**
* Gets the instance type from a constructor, abstract constructor, or SafeInjectionToken
*/
type ProviderInstanceType<T> =
T extends SafeInjectionToken<any>
? InstanceType<SafeInjectionTokenType<T>>
: T extends Constructor<any> | AbstractConstructor<any>
? InstanceType<T>
: never;
/**
* Represents a dependency provided with the useClass option.
*/
type SafeClassProvider<
A extends AbstractConstructor<any> | SafeInjectionToken<any>,
I extends Constructor<ProviderInstanceType<A>>,
D extends MapParametersToDeps<ConstructorParameters<I>>,
> = {
provide: A;
useClass: I;
deps: D;
};
/**
* Represents a dependency provided with the useValue option.
*/
type SafeValueProvider<A extends SafeInjectionToken<any>, V extends SafeInjectionTokenType<A>> = {
provide: A;
useValue: V;
};
/**
* Represents a dependency provided with the useFactory option.
*/
type SafeFactoryProvider<
A extends AbstractConstructor<any> | SafeInjectionToken<any>,
I extends (...args: any) => ProviderInstanceType<A>,
D extends MapParametersToDeps<Parameters<I>>,
> = {
provide: A;
useFactory: I;
deps: D;
multi?: boolean;
};
/**
* Represents a dependency provided with the useExisting option.
*/
type SafeExistingProvider<
A extends Constructor<any> | AbstractConstructor<any> | SafeInjectionToken<any>,
I extends Constructor<ProviderInstanceType<A>> | AbstractConstructor<ProviderInstanceType<A>>,
> = {
provide: A;
useExisting: I;
};
/**
* Represents a dependency where there is no abstract token, the token is the implementation
*/
type SafeConcreteProvider<
I extends Constructor<any>,
D extends MapParametersToDeps<ConstructorParameters<I>>,
> = {
provide: I;
deps: D;
};
/**
* If useAngularDecorators: true is specified, do not require a deps array.
* This is a manual override for where @Injectable decorators are used
*/
type UseAngularDecorators<T extends { deps: any }> = Omit<T, "deps"> & {
useAngularDecorators: true;
};
/**
* Represents a type with a deps array that may optionally be overridden with useAngularDecorators
*/
type AllowAngularDecorators<T extends { deps: any }> = T | UseAngularDecorators<T>;
/**
* A factory function that creates a provider for the ngModule providers array.
* This (almost) guarantees type safety for your provider definition. It does nothing at runtime.
* Warning: the useAngularDecorators option provides an override where your class uses the Injectable decorator,
* however this cannot be enforced by the type system and will not cause an error if the decorator is not used.
* @example safeProvider({ provide: MyService, useClass: DefaultMyService, deps: [AnotherService] })
* @param provider Your provider object in the usual shape (e.g. using useClass, useValue, useFactory, etc.)
* @returns The exact same object without modification (pass-through).
*/
export const safeProvider = <
// types for useClass
AClass extends AbstractConstructor<any> | SafeInjectionToken<any>,
IClass extends Constructor<ProviderInstanceType<AClass>>,
DClass extends MapParametersToDeps<ConstructorParameters<IClass>>,
// types for useValue
AValue extends SafeInjectionToken<any>,
VValue extends SafeInjectionTokenType<AValue>,
// types for useFactory
AFactory extends AbstractConstructor<any> | SafeInjectionToken<any>,
IFactory extends (...args: any) => ProviderInstanceType<AFactory>,
DFactory extends MapParametersToDeps<Parameters<IFactory>>,
// types for useExisting
AExisting extends Constructor<any> | AbstractConstructor<any> | SafeInjectionToken<any>,
IExisting extends
| Constructor<ProviderInstanceType<AExisting>>
| AbstractConstructor<ProviderInstanceType<AExisting>>,
// types for no token
IConcrete extends Constructor<any>,
DConcrete extends MapParametersToDeps<ConstructorParameters<IConcrete>>,
>(
provider:
| AllowAngularDecorators<SafeClassProvider<AClass, IClass, DClass>>
| SafeValueProvider<AValue, VValue>
| AllowAngularDecorators<SafeFactoryProvider<AFactory, IFactory, DFactory>>
| SafeExistingProvider<AExisting, IExisting>
| AllowAngularDecorators<SafeConcreteProvider<IConcrete, DConcrete>>
| Constructor<unknown>,
): SafeProvider => provider as SafeProvider;

View File

@ -11,7 +11,7 @@ class FooFactory {
}
abstract class FooService {
createFoo: (str: string) => string;
abstract createFoo(str: string): string;
}
class DefaultFooService implements FooService {
@ -29,7 +29,7 @@ class BarFactory {
}
abstract class BarService {
createBar: (num: number) => number;
abstract createBar(num: number): number;
}
class DefaultBarService implements BarService {

View File

@ -3,7 +3,13 @@ import { Pipe, PipeTransform } from "@angular/core";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
/**
* Temporarily duplicate this pipe
* Localizes the specified string.
*
* @example
* {{ 'key' | i18n }}
*
* @example
* {{ 'key' | i18n: 'param1' }}
*/
@Pipe({
name: "i18n",

View File

@ -0,0 +1,2 @@
export * from "./di";
export * from "./i18n.pipe";

View File

@ -0,0 +1,10 @@
{
"extends": "../../shared/tsconfig",
"compilerOptions": {
"paths": {
"@bitwarden/common/*": ["../../common/src/*"]
}
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

View File

@ -15,6 +15,7 @@
"@bitwarden/generator-navigation": ["../tools/generator/extensions/navigation/src"],
"@bitwarden/key-management": ["../key-management/src"],
"@bitwarden/platform": ["../platform/src"],
"@bitwarden/ui-common": ["../ui/common/src"],
"@bitwarden/vault": ["../vault/src"]
}
},

View File

@ -20,6 +20,7 @@
"@bitwarden/angular/*": ["./libs/angular/src/*"],
"@bitwarden/auth": ["./libs/auth/src"],
"@bitwarden/billing": ["./libs/billing/src"],
"@bitwarden/bit-common/*": ["./bitwarden_license/bit-common/src/*"],
"@bitwarden/common/*": ["./libs/common/src/*"],
"@bitwarden/components": ["./libs/components/src"],
"@bitwarden/generator-components": ["./libs/tools/generator/components/src"],
@ -27,18 +28,18 @@
"@bitwarden/generator-history": ["./libs/tools/generator/extensions/history/src"],
"@bitwarden/generator-legacy": ["./libs/tools/generator/extensions/legacy/src"],
"@bitwarden/generator-navigation": ["./libs/tools/generator/extensions/navigation/src"],
"@bitwarden/vault-export-core": [".libs/tools/export/vault-export/vault-export-core/src"],
"@bitwarden/vault-export-ui": [".libs/tools/export/vault-export/vault-export-ui/src"],
"@bitwarden/importer/core": ["./libs/importer/src"],
"@bitwarden/importer/ui": ["./libs/importer/src/components"],
"@bitwarden/send-ui": [".libs/tools/send/send-ui/src"],
"@bitwarden/tools-card": [".libs/tools/card/src"],
"@bitwarden/platform": ["./libs/platform/src"],
"@bitwarden/node/*": ["./libs/node/src/*"],
"@bitwarden/vault": ["./libs/vault/src"],
"@bitwarden/key-management": ["./libs/key-management/src"],
"@bitwarden/key-management/angular": ["./libs/key-management/src/angular"],
"@bitwarden/bit-common/*": ["./bitwarden_license/bit-common/src/*"]
"@bitwarden/node/*": ["./libs/node/src/*"],
"@bitwarden/platform": ["./libs/platform/src"],
"@bitwarden/send-ui": [".libs/tools/send/send-ui/src"],
"@bitwarden/tools-card": [".libs/tools/card/src"],
"@bitwarden/ui-common": ["./libs/ui/common/src"],
"@bitwarden/vault-export-core": [".libs/tools/export/vault-export/vault-export-core/src"],
"@bitwarden/vault-export-ui": [".libs/tools/export/vault-export/vault-export-ui/src"],
"@bitwarden/vault": ["./libs/vault/src"]
},
"plugins": [
{

View File

@ -18,9 +18,10 @@
"paths": {
"@bitwarden/admin-console/common": ["./libs/admin-console/src/common"],
"@bitwarden/angular/*": ["./libs/angular/src/*"],
"@bitwarden/auth/common": ["./libs/auth/src/common"],
"@bitwarden/auth/angular": ["./libs/auth/src/angular"],
"@bitwarden/auth/common": ["./libs/auth/src/common"],
"@bitwarden/billing": ["./libs/billing/src"],
"@bitwarden/bit-common/*": ["./bitwarden_license/bit-common/src/*"],
"@bitwarden/common/*": ["./libs/common/src/*"],
"@bitwarden/components": ["./libs/components/src"],
"@bitwarden/generator-components": ["./libs/tools/generator/components/src"],
@ -28,19 +29,19 @@
"@bitwarden/generator-history": ["./libs/tools/generator/extensions/history/src"],
"@bitwarden/generator-legacy": ["./libs/tools/generator/extensions/legacy/src"],
"@bitwarden/generator-navigation": ["./libs/tools/generator/extensions/navigation/src"],
"@bitwarden/vault-export-core": ["./libs/tools/export/vault-export/vault-export-core/src"],
"@bitwarden/vault-export-ui": ["./libs/tools/export/vault-export/vault-export-ui/src"],
"@bitwarden/importer/core": ["./libs/importer/src"],
"@bitwarden/importer/ui": ["./libs/importer/src/components"],
"@bitwarden/key-management": ["./libs/key-management/src"],
"@bitwarden/key-management/angular": ["./libs/key-management/src/angular"],
"@bitwarden/node/*": ["./libs/node/src/*"],
"@bitwarden/platform": ["./libs/platform/src"],
"@bitwarden/send-ui": ["./libs/tools/send/send-ui/src"],
"@bitwarden/tools-card": ["./libs/tools/card/src"],
"@bitwarden/node/*": ["./libs/node/src/*"],
"@bitwarden/web-vault/*": ["./apps/web/src/*"],
"@bitwarden/ui-common": ["./libs/ui/common/src"],
"@bitwarden/vault-export-core": ["./libs/tools/export/vault-export/vault-export-core/src"],
"@bitwarden/vault-export-ui": ["./libs/tools/export/vault-export/vault-export-ui/src"],
"@bitwarden/vault": ["./libs/vault/src"],
"@bitwarden/bit-common/*": ["./bitwarden_license/bit-common/src/*"]
"@bitwarden/web-vault/*": ["./apps/web/src/*"]
},
"plugins": [
{