diff --git a/apps/web/src/scss/forms.scss b/apps/web/src/scss/forms.scss index 9404bc94031..18cc5e9cb7f 100644 --- a/apps/web/src/scss/forms.scss +++ b/apps/web/src/scss/forms.scss @@ -20,7 +20,7 @@ input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: -cancel-button; } -label:not(.form-check-label):not(.btn), +label:not(.form-check-label):not(.btn):not(:has(bit-label)), label.bold { font-weight: 600; @include themify($themes) { diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component.html b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component.html index 0d0ca04f926..15120eed92a 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component.html +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/domain-add-edit-dialog.component.html @@ -33,12 +33,10 @@ - - + > {{ "spMetadataUrl" | i18n }} - - + > ) {} + + @HostBinding("class") @Input() get classList() { + return ["tw-truncate"]; + } + + @HostBinding("title") get title() { + return this.elementRef.nativeElement.textContent; + } + + @HostBinding() @Input() id = `bit-label-${nextId++}`; +} diff --git a/libs/components/src/form-field/error.component.ts b/libs/components/src/form-field/error.component.ts index 39005903e6d..ecfa70b2d16 100644 --- a/libs/components/src/form-field/error.component.ts +++ b/libs/components/src/form-field/error.component.ts @@ -9,7 +9,7 @@ let nextId = 0; selector: "bit-error", template: ` {{ displayError }}`, host: { - class: "tw-block tw-mt-1 tw-text-danger", + class: "tw-block tw-mt-1 tw-text-danger tw-text-xs", "aria-live": "assertive", }, }) diff --git a/libs/components/src/form-field/form-field.component.html b/libs/components/src/form-field/form-field.component.html index 5c46fb557b4..b2b08475771 100644 --- a/libs/components/src/form-field/form-field.component.html +++ b/libs/components/src/form-field/form-field.component.html @@ -1,14 +1,54 @@ - - - ({{ "required" | i18n }}) - - - - + + + + + + + + + + + - - - + + + + + + + ({{ "required" | i18n }}) + + + + diff --git a/libs/components/src/form-field/form-field.component.ts b/libs/components/src/form-field/form-field.component.ts index 6fcb4090ddd..87deb3cfe2d 100644 --- a/libs/components/src/form-field/form-field.component.ts +++ b/libs/components/src/form-field/form-field.component.ts @@ -3,19 +3,19 @@ import { AfterContentChecked, Component, ContentChild, - ContentChildren, HostBinding, + HostListener, Input, - QueryList, ViewChild, + signal, } from "@angular/core"; import { BitHintComponent } from "../form-control/hint.component"; +import { BitLabel } from "../form-control/label.directive"; +import { inputBorderClasses } from "../input/input.directive"; import { BitErrorComponent } from "./error.component"; import { BitFormFieldControl } from "./form-field-control"; -import { BitPrefixDirective } from "./prefix.directive"; -import { BitSuffixDirective } from "./suffix.directive"; @Component({ selector: "bit-form-field", @@ -24,12 +24,10 @@ import { BitSuffixDirective } from "./suffix.directive"; export class BitFormFieldComponent implements AfterContentChecked { @ContentChild(BitFormFieldControl) input: BitFormFieldControl; @ContentChild(BitHintComponent) hint: BitHintComponent; + @ContentChild(BitLabel) label: BitLabel; @ViewChild(BitErrorComponent) error: BitErrorComponent; - @ContentChildren(BitPrefixDirective) prefixChildren: QueryList; - @ContentChildren(BitSuffixDirective) suffixChildren: QueryList; - private _disableMargin = false; @Input() set disableMargin(value: boolean | "") { this._disableMargin = coerceBooleanProperty(value); @@ -38,11 +36,50 @@ export class BitFormFieldComponent implements AfterContentChecked { return this._disableMargin; } + get inputBorderClasses(): string { + const shouldFocusBorderAppear = !this.buttonIsFocused(); + + const groupClasses = [ + this.input.hasError + ? "group-hover/bit-form-field:tw-border-danger-700" + : "group-hover/bit-form-field:tw-border-primary-500", + "group-focus-within/bit-form-field:tw-outline-none", + shouldFocusBorderAppear ? "group-focus-within/bit-form-field:tw-border-2" : "", + shouldFocusBorderAppear ? "group-focus-within/bit-form-field:tw-border-primary-500" : "", + shouldFocusBorderAppear + ? "group-focus-within/bit-form-field:group-hover/bit-form-field:tw-border-primary-500" + : "", + ]; + + const baseInputBorderClasses = inputBorderClasses(this.input.hasError); + + const borderClasses = baseInputBorderClasses.concat(groupClasses); + + return borderClasses.join(" "); + } + @HostBinding("class") get classList() { return ["tw-block"].concat(this.disableMargin ? [] : ["tw-mb-6"]); } + /** + * If the currently focused element is a button, then we don't want to show focus on the + * input field itself. + * + * This is necessary because the `tw-group/bit-form-field` wraps the input and any prefix/suffix + * buttons + */ + protected buttonIsFocused = signal(false); + @HostListener("focusin", ["$event.target"]) + onFocusIn(target: HTMLElement) { + this.buttonIsFocused.set(target.matches("button")); + } + @HostListener("focusout") + onFocusOut() { + this.buttonIsFocused.set(false); + } + ngAfterContentChecked(): void { if (this.error) { this.input.ariaDescribedBy = this.error.id; diff --git a/libs/components/src/form-field/form-field.stories.ts b/libs/components/src/form-field/form-field.stories.ts index 305b514266e..f95f2a547a5 100644 --- a/libs/components/src/form-field/form-field.stories.ts +++ b/libs/components/src/form-field/form-field.stories.ts @@ -16,6 +16,7 @@ import { ButtonModule } from "../button"; import { CheckboxModule } from "../checkbox"; import { IconButtonModule } from "../icon-button"; import { InputModule } from "../input/input.module"; +import { LinkModule } from "../link"; import { RadioButtonModule } from "../radio-button"; import { SelectModule } from "../select"; import { I18nMockService } from "../utils/i18n-mock.service"; @@ -39,6 +40,7 @@ export default { CheckboxModule, RadioButtonModule, SelectModule, + LinkModule, ], providers: [ { @@ -74,6 +76,7 @@ const defaultFormObj = fb.group({ email: ["", [Validators.required, Validators.email, forbiddenNameValidator(/bit/i)]], terms: [false, [Validators.requiredTrue]], updates: ["yes"], + file: [""], }); // Custom error message, `message` is shown as the error message @@ -96,7 +99,7 @@ export const Default: Story = { submit: submit, ...args, }, - template: ` + template: /*html*/ ` Label @@ -108,13 +111,58 @@ export const Default: Story = { }), }; +export const LabelWithIcon: Story = { + render: (args) => ({ + props: { + formObj: defaultFormObj, + submit: submit, + ...args, + }, + template: /*html*/ ` + + + + Label + + + + + + Optional Hint + + + `, + }), +}; + +export const LongLabel: Story = { + render: (args) => ({ + props: { + formObj: defaultFormObj, + submit: submit, + ...args, + }, + template: /*html*/ ` + + + + Hello I am a very long label with lots of very cool helpful information + + + Optional Hint + + + `, + }), +}; + export const Required: Story = { render: (args) => ({ props: { formObj: formObj, ...args, }, - template: ` + template: /*html*/ ` Label @@ -134,7 +182,7 @@ export const Hint: Story = { formObj: formObj, ...args, }, - template: ` + template: /*html*/ ` FormControl @@ -147,7 +195,7 @@ export const Hint: Story = { export const Disabled: Story = { render: (args) => ({ props: args, - template: ` + template: /*html*/ ` Label @@ -160,7 +208,7 @@ export const Disabled: Story = { export const Readonly: Story = { render: (args) => ({ props: args, - template: ` + template: /*html*/ ` Input @@ -178,7 +226,7 @@ export const Readonly: Story = { export const InputGroup: Story = { render: (args) => ({ props: args, - template: ` + template: /*html*/ ` Label @@ -193,13 +241,14 @@ export const InputGroup: Story = { export const ButtonInputGroup: Story = { render: (args) => ({ props: args, - template: ` + template: /*html*/ ` - + Label + - - - + + + Apply @@ -211,14 +260,32 @@ export const ButtonInputGroup: Story = { export const DisabledButtonInputGroup: Story = { render: (args) => ({ props: args, - template: ` + template: /*html*/ ` Label - + - - - + + + + Apply + + + `, + }), + args: {}, +}; + +export const PartiallyDisabledButtonInputGroup: Story = { + render: (args) => ({ + props: args, + template: /*html*/ ` + + Label + + + + Apply @@ -230,7 +297,7 @@ export const DisabledButtonInputGroup: Story = { export const Select: Story = { render: (args: BitFormFieldComponent) => ({ props: args, - template: ` + template: /*html*/ ` Label @@ -246,7 +313,7 @@ export const Select: Story = { export const AdvancedSelect: Story = { render: (args: BitFormFieldComponent) => ({ props: args, - template: ` + template: /*html*/ ` Label @@ -258,10 +325,40 @@ export const AdvancedSelect: Story = { }), }; +export const FileInput: Story = { + render: (args) => ({ + props: { + formObj: defaultFormObj, + submit: submit, + ...args, + }, + template: /*html*/ ` + + + File + + + Choose File + + No file chosen + + + + + `, + }), +}; + export const Textarea: Story = { render: (args: BitFormFieldComponent) => ({ props: args, - template: ` + template: /*html*/ ` Textarea diff --git a/libs/components/src/form-field/multi-select.stories.ts b/libs/components/src/form-field/multi-select.stories.ts index 9248f7ab473..0993925364d 100644 --- a/libs/components/src/form-field/multi-select.stories.ts +++ b/libs/components/src/form-field/multi-select.stories.ts @@ -66,9 +66,9 @@ export const actionsData = { }; const fb = new FormBuilder(); -const formObjFactory = () => +const formObjFactory = (isDisabled = false) => fb.group({ - select: [[], [Validators.required]], + select: fb.control({ value: [], disabled: isDisabled }, { validators: [Validators.required] }), }); function submit(formObj: FormGroup) { @@ -85,7 +85,7 @@ export const Loading: Story = { ...args, onItemsConfirmed: actionsData.onItemsConfirmed, }, - template: ` + template: /*html*/ ` {{ name }} @@ -100,7 +100,6 @@ export const Loading: Story = { {{ hint }} - Submit `, }), @@ -113,8 +112,33 @@ export const Loading: Story = { }; export const Disabled: Story = { - ...Loading, + render: (args) => ({ + props: { + formObj: formObjFactory(true), + submit: submit, + ...args, + onItemsConfirmed: actionsData.onItemsConfirmed, + }, + template: /*html*/ ` + + + {{ name }} + + + {{ hint }} + + + `, + }), args: { + baseItems: [] as any, name: "Disabled", disabled: true, hint: "This is what a disabled multi-select looks like", @@ -269,34 +293,3 @@ export const RemoveSelected: Story = { removeSelectedItems: true, }, }; - -export const Standalone: Story = { - render: (args) => ({ - props: { - ...args, - onItemsConfirmed: actionsData.onItemsConfirmed, - }, - template: ` - - - `, - }), - args: { - baseItems: [ - { id: "1", listName: "Group 1", labelName: "Group 1", icon: "bwi-family" }, - { id: "2", listName: "Group 2", labelName: "Group 2", icon: "bwi-family" }, - { id: "3", listName: "Group 3", labelName: "Group 3", icon: "bwi-family" }, - { id: "4", listName: "Group 4", labelName: "Group 4", icon: "bwi-family" }, - { id: "5", listName: "Group 5", labelName: "Group 5", icon: "bwi-family" }, - { id: "6", listName: "Group 6", labelName: "Group 6", icon: "bwi-family" }, - { id: "7", listName: "Group 7", labelName: "Group 7", icon: "bwi-family" }, - ], - removeSelectedItems: true, - }, -}; diff --git a/libs/components/src/form-field/password-input-toggle.stories.ts b/libs/components/src/form-field/password-input-toggle.stories.ts index 412213110f8..094f939e0ea 100644 --- a/libs/components/src/form-field/password-input-toggle.stories.ts +++ b/libs/components/src/form-field/password-input-toggle.stories.ts @@ -43,7 +43,7 @@ type Story = StoryObj; export const Default: Story = { render: (args) => ({ props: args, - template: ` + template: /*html*/ ` Password @@ -58,7 +58,7 @@ export const Default: Story = { export const Binding: Story = { render: (args) => ({ props: args, - template: ` + template: /*html*/ ` Password diff --git a/libs/components/src/form-field/prefix.directive.ts b/libs/components/src/form-field/prefix.directive.ts index 6e1e15fd207..371cc51c78b 100644 --- a/libs/components/src/form-field/prefix.directive.ts +++ b/libs/components/src/form-field/prefix.directive.ts @@ -1,51 +1,30 @@ -import { Directive, HostBinding, Input, OnInit, Optional } from "@angular/core"; +import { Directive, HostBinding, Input, Optional } from "@angular/core"; -import { ButtonLikeAbstraction } from "../shared/button-like.abstraction"; +import { BitIconButtonComponent } from "../icon-button/icon-button.component"; -export const PrefixClasses = [ - "tw-bg-background-alt", - "tw-border", - "tw-border-solid", - "tw-border-secondary-600", - "tw-text-muted", - "tw-rounded-none", -]; - -export const PrefixButtonClasses = [ - "hover:tw-bg-text-muted", - "hover:tw-text-contrast", - "disabled:tw-opacity-100", - "disabled:tw-bg-secondary-100", - "disabled:hover:tw-bg-secondary-100", - "disabled:hover:tw-text-muted", - "focus-visible:tw-ring-primary-700", - - "focus-visible:tw-border-primary-700", - "focus-visible:tw-ring-1", - "focus-visible:tw-ring-inset", - "focus-visible:tw-ring-primary-700", - "focus-visible:tw-z-10", -]; - -export const PrefixStaticContentClasses = ["tw-block", "tw-px-3", "tw-py-1.5"]; +import { BitFormFieldComponent } from "./form-field.component"; @Directive({ selector: "[bitPrefix]", }) -export class BitPrefixDirective implements OnInit { - constructor(@Optional() private buttonComponent: ButtonLikeAbstraction) {} - +export class BitPrefixDirective { @HostBinding("class") @Input() get classList() { - return PrefixClasses.concat([ - "tw-border-r-0", - "first:tw-rounded-l", - - "focus-visible:tw-border-r", - "focus-visible:tw-mr-[-1px]", - ]).concat(this.buttonComponent != undefined ? PrefixButtonClasses : PrefixStaticContentClasses); + return ["tw-text-muted"]; } - ngOnInit(): void { - this.buttonComponent?.setButtonType("unstyled"); + @HostBinding("attr.aria-describedby") + get ariaDescribedBy() { + return this.parentFormField?.label?.id || null; + } + + constructor( + @Optional() private parentFormField: BitFormFieldComponent, + @Optional() private iconButtonComponent: BitIconButtonComponent, + ) {} + + ngOnInit() { + if (this.iconButtonComponent) { + this.iconButtonComponent.size = "small"; + } } } diff --git a/libs/components/src/form-field/suffix.directive.ts b/libs/components/src/form-field/suffix.directive.ts index c7fdfa2eb0e..e79d78aeeed 100644 --- a/libs/components/src/form-field/suffix.directive.ts +++ b/libs/components/src/form-field/suffix.directive.ts @@ -1,26 +1,30 @@ import { Directive, HostBinding, Input, Optional } from "@angular/core"; -import { ButtonLikeAbstraction } from "../shared/button-like.abstraction"; +import { BitIconButtonComponent } from "../icon-button/icon-button.component"; -import { PrefixButtonClasses, PrefixClasses, PrefixStaticContentClasses } from "./prefix.directive"; +import { BitFormFieldComponent } from "./form-field.component"; @Directive({ selector: "[bitSuffix]", }) export class BitSuffixDirective { - constructor(@Optional() private buttonComponent: ButtonLikeAbstraction) {} - @HostBinding("class") @Input() get classList() { - return PrefixClasses.concat([ - "tw-border-l-0", - "last:tw-rounded-r", - - "focus-visible:tw-border-l", - "focus-visible:tw-ml-[-1px]", - ]).concat(this.buttonComponent != undefined ? PrefixButtonClasses : PrefixStaticContentClasses); + return ["tw-text-muted"]; } - ngOnInit(): void { - this.buttonComponent?.setButtonType("unstyled"); + @HostBinding("attr.aria-describedby") + get ariaDescribedBy() { + return this.parentFormField?.label?.id || null; + } + + constructor( + @Optional() private parentFormField: BitFormFieldComponent, + @Optional() private iconButtonComponent: BitIconButtonComponent, + ) {} + + ngOnInit() { + if (this.iconButtonComponent) { + this.iconButtonComponent.size = "small"; + } } } diff --git a/libs/components/src/icon-button/icon-button.component.ts b/libs/components/src/icon-button/icon-button.component.ts index 54f6dfda963..0b26ebd948d 100644 --- a/libs/components/src/icon-button/icon-button.component.ts +++ b/libs/components/src/icon-button/icon-button.component.ts @@ -163,10 +163,6 @@ export class BitIconButtonComponent implements ButtonLikeAbstraction, FocusableE @Input() loading = false; @Input() disabled = false; - setButtonType(value: "primary" | "secondary" | "danger" | "unstyled") { - this.buttonType = value; - } - getFocusTarget() { return this.elementRef.nativeElement; } diff --git a/libs/components/src/input/input.directive.ts b/libs/components/src/input/input.directive.ts index 27c7d8175d1..57664c98f05 100644 --- a/libs/components/src/input/input.directive.ts +++ b/libs/components/src/input/input.directive.ts @@ -11,41 +11,43 @@ import { import { NgControl, Validators } from "@angular/forms"; import { BitFormFieldControl, InputTypes } from "../form-field/form-field-control"; +import { BitFormFieldComponent } from "../form-field/form-field.component"; // Increments for each instance of this component let nextId = 0; +export function inputBorderClasses(error: boolean) { + return [ + "tw-border", + "!tw-border-solid", + error ? "tw-border-danger-600" : "tw-border-secondary-500", + "focus:tw-outline-none", + ]; +} + @Directive({ selector: "input[bitInput], select[bitInput], textarea[bitInput]", providers: [{ provide: BitFormFieldControl, useExisting: BitInputDirective }], }) export class BitInputDirective implements BitFormFieldControl { @HostBinding("class") @Input() get classList() { - return [ + const classes = [ "tw-block", "tw-w-full", - "tw-px-3", - "tw-py-1.5", - "tw-bg-background-alt", - "tw-border", - "tw-border-solid", - this.hasError ? "tw-border-danger-600" : "tw-border-secondary-600", + "tw-h-full", "tw-text-main", "tw-placeholder-text-muted", - // Rounded - "tw-rounded-none", - "first:tw-rounded-l", - "last:tw-rounded-r", - // Focus + "tw-bg-background", + "tw-border-none", "focus:tw-outline-none", - "focus:tw-border-primary-700", - "focus:tw-ring-1", - "focus:tw-ring-inset", - "focus:tw-ring-primary-700", - "focus:tw-z-10", - "disabled:tw-bg-secondary-100", "[&:is(input,textarea):read-only]:tw-bg-secondary-100", - ].filter((s) => s != ""); + ]; + + if (this.parentFormField === null) { + classes.push(...inputBorderClasses(this.hasError), ...this.standaloneInputClasses); + } + + return classes.filter((s) => s != ""); } @HostBinding() @Input() id = `bit-input-${nextId++}`; @@ -105,6 +107,7 @@ export class BitInputDirective implements BitFormFieldControl { @Optional() @Self() private ngControl: NgControl, private ngZone: NgZone, private elementRef: ElementRef, + @Optional() private parentFormField: BitFormFieldComponent, ) {} focus() { @@ -114,4 +117,23 @@ export class BitInputDirective implements BitFormFieldControl { this.elementRef.nativeElement.focus(); }); } + + get standaloneInputClasses() { + return [ + "tw-px-3", + "tw-py-2", + "tw-rounded-lg", + // Hover + this.hasError ? "hover:tw-border-danger-700" : "hover:tw-border-primary-500", + // Focus + "focus:hover:tw-border-primary-500", + "disabled:tw-bg-secondary-100", + "disabled:hover:tw-border-secondary-500", + "focus:tw-border-primary-500", + "focus:tw-ring-1", + "focus:tw-ring-inset", + "focus:tw-ring-primary-500", + "focus:tw-z-10", + ]; + } } diff --git a/libs/components/src/multi-select/multi-select.component.ts b/libs/components/src/multi-select/multi-select.component.ts index b0a2cf613b6..249c0473c4c 100644 --- a/libs/components/src/multi-select/multi-select.component.ts +++ b/libs/components/src/multi-select/multi-select.component.ts @@ -1,3 +1,4 @@ +import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { hasModifierKey } from "@angular/cdk/keycodes"; import { Component, @@ -39,7 +40,7 @@ export class MultiSelectComponent implements OnInit, BitFormFieldControl, Contro @Input() removeSelectedItems = false; @Input() placeholder: string; @Input() loading = false; - @Input() disabled = false; + @Input({ transform: coerceBooleanProperty }) disabled?: boolean; // Internal tracking of selected items protected selectedItems: SelectItemView[]; diff --git a/libs/components/src/multi-select/scss/bw.theme.scss b/libs/components/src/multi-select/scss/bw.theme.scss index b567c725924..8e12c51ba72 100644 --- a/libs/components/src/multi-select/scss/bw.theme.scss +++ b/libs/components/src/multi-select/scss/bw.theme.scss @@ -9,19 +9,18 @@ $ng-select-highlight: rgb(var(--color-primary-700)) !default; $ng-select-primary-text: rgb(var(--color-text-main)) !default; $ng-select-disabled-text: rgb(var(--color-secondary-100)) !default; $ng-select-border: rgb(var(--color-secondary-600)) !default; -$ng-select-border-radius: 4px !default; -$ng-select-bg: rgb(var(--color-background-alt)) !default; +$ng-select-border-radius: 0.5rem !default; +$ng-select-bg: rgb(var(--color-background)) !default; $ng-select-selected: transparent !default; -$ng-select-selected-alt: rgb(var(--color-text-main) / 0.06) !default; $ng-select-selected-text: $ng-select-primary-text !default; -$ng-select-marked: rgb(var(--color-text-main) / 0.12) !default; +$ng-select-marked: rgb(var(--color-primary-100)) !default; $ng-select-marked-text: $ng-select-primary-text !default; $ng-select-box-shadow: none !default; $ng-select-placeholder: rgb(var(--color-text-muted)) !default; -$ng-select-height: 35px !default; -$ng-select-value-padding-left: 10px !default; +$ng-select-height: 100%; +$ng-select-value-padding-left: 1rem !default; $ng-select-value-font-size: 0.9em !default; $ng-select-value-text: $ng-select-primary-text !default; @@ -31,7 +30,7 @@ $ng-select-dropdown-optgroup-text: rgb(var(--color-text-muted)) !default; $ng-select-dropdown-optgroup-marked: $ng-select-dropdown-optgroup-text !default; $ng-select-dropdown-option-bg: $ng-select-dropdown-bg !default; $ng-select-dropdown-option-text: $ng-select-primary-text !default; -$ng-select-dropdown-option-disabled: rgb(var(--color-text-muted) / 0.6) !default; +$ng-select-dropdown-option-disabled: rgb(var(--color-secondary-300)) !default; $ng-select-input-text: $ng-select-primary-text !default; @@ -41,12 +40,12 @@ $ng-clear-icon-hover: rgb(var(--color-text-main)) !default; $ng-dropdown-shadow: rgb(var(--color-secondary-100)) !default; .ng-select { + height: $ng-select-height; &.ng-select-opened { > .ng-select-container { - background: $ng-select-bg; - border-color: $ng-select-border; + background: transparent; &:hover { - box-shadow: none; + box-shadow: $ng-select-box-shadow; } .ng-arrow { top: -2px; @@ -100,11 +99,11 @@ $ng-dropdown-shadow: rgb(var(--color-secondary-100)) !default; color: $ng-select-primary-text; background-color: $ng-select-bg; border-radius: $ng-select-border-radius; - border: 1px solid $ng-select-border; - min-height: $ng-select-height; + border: none; + height: $ng-select-height; align-items: center; &:hover { - box-shadow: 0 1px 0 $ng-dropdown-shadow; + box-shadow: $ng-select-box-shadow; } .ng-value-container { align-items: center; @@ -154,9 +153,7 @@ $ng-dropdown-shadow: rgb(var(--color-secondary-100)) !default; .ng-select-container { .ng-value-container { padding-top: 5px; - padding-left: 7px; @include rtl { - padding-right: 7px; padding-left: 0; } .ng-value { @@ -206,20 +203,8 @@ $ng-dropdown-shadow: rgb(var(--color-secondary-100)) !default; } } } - .ng-input { - padding: 0 0 3px 3px; - @include rtl { - padding: 0 3px 3px 0; - } - } .ng-placeholder { - top: 5px; - padding-bottom: 5px; - padding-left: 3px; - @include rtl { - padding-right: 3px; - padding-left: 0; - } + padding-bottom: 0.25rem; } } } @@ -230,6 +215,8 @@ $ng-dropdown-shadow: rgb(var(--color-secondary-100)) !default; &:hover .ng-clear { color: $ng-clear-icon-hover; } + border-radius: $ng-select-border-radius; + text-align: center; } .ng-spinner-zone { padding: 5px 5px 0 0; @@ -262,7 +249,8 @@ $ng-dropdown-shadow: rgb(var(--color-secondary-100)) !default; z-index: 2050 !important; background-color: $ng-select-dropdown-bg; border: 1px solid $ng-select-dropdown-border; - box-shadow: 0 1px 0 $ng-dropdown-shadow; + border-radius: $ng-select-border-radius; + box-shadow: $ng-select-box-shadow; left: 0; &.ng-select-top { bottom: 100%; @@ -335,6 +323,8 @@ $ng-dropdown-shadow: rgb(var(--color-secondary-100)) !default; padding: 5px 7px; } .ng-dropdown-panel-items { + border-radius: $ng-select-border-radius; + background: $ng-select-bg; .ng-optgroup { user-select: none; padding: 8px 10px; @@ -357,10 +347,7 @@ $ng-dropdown-shadow: rgb(var(--color-secondary-100)) !default; .ng-option { background-color: $ng-select-dropdown-option-bg; color: $ng-select-dropdown-option-text; - padding: 8px 10px; - &.ng-option-selected { - background-color: $ng-select-selected-alt; - } + padding: 0.375rem 0.75rem; &.ng-option-selected.ng-option-marked { background-color: $ng-select-marked; } diff --git a/libs/components/src/search/search.component.html b/libs/components/src/search/search.component.html index 82bd153e10d..ca44f295358 100644 --- a/libs/components/src/search/search.component.html +++ b/libs/components/src/search/search.component.html @@ -13,7 +13,7 @@ type="search" [id]="id" [placeholder]="placeholder ?? ('search' | i18n)" - class="tw-rounded-l tw-pl-9" + class="tw-pl-9" [ngModel]="searchText" (ngModelChange)="onChange($event)" (blur)="onTouch()" diff --git a/libs/components/src/select/select.component.ts b/libs/components/src/select/select.component.ts index 34008667199..bdd28e512e2 100644 --- a/libs/components/src/select/select.component.ts +++ b/libs/components/src/select/select.component.ts @@ -54,7 +54,7 @@ export class SelectComponent implements BitFormFieldControl, ControlValueAcce this.selectedOption = this.findSelectedOption(this.items, this.selectedValue); } - @HostBinding("class") protected classes = ["tw-block", "tw-w-full"]; + @HostBinding("class") protected classes = ["tw-block", "tw-w-full", "tw-h-full"]; @HostBinding() @Input() diff --git a/libs/components/src/select/select.stories.ts b/libs/components/src/select/select.stories.ts index a90d534d340..4bc85d8dba2 100644 --- a/libs/components/src/select/select.stories.ts +++ b/libs/components/src/select/select.stories.ts @@ -2,6 +2,7 @@ import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { FormFieldModule } from "../form-field"; import { MultiSelectComponent } from "../multi-select/multi-select.component"; import { I18nMockService } from "../utils/i18n-mock.service"; @@ -13,7 +14,7 @@ export default { component: SelectComponent, decorators: [ moduleMetadata({ - imports: [SelectModule], + imports: [SelectModule, FormFieldModule], providers: [ { provide: I18nService, @@ -44,12 +45,17 @@ export const Default: Story = { props: { ...args, }, - template: ` - - - - - `, + template: /*html*/ ` + + Choose a value + + + + + + + + `, }), args: {}, }; diff --git a/libs/components/src/shared/button-like.abstraction.ts b/libs/components/src/shared/button-like.abstraction.ts index e422bd4f7da..82fdc578f25 100644 --- a/libs/components/src/shared/button-like.abstraction.ts +++ b/libs/components/src/shared/button-like.abstraction.ts @@ -3,5 +3,4 @@ export type ButtonType = "primary" | "secondary" | "danger" | "unstyled"; export abstract class ButtonLikeAbstraction { loading: boolean; disabled: boolean; - setButtonType: (value: ButtonType) => void; }