diff --git a/libs/theme-core/src/theming/theming.scss b/libs/theme-core/src/theming/theming.scss index fd56baea..3a0ada6f 100644 --- a/libs/theme-core/src/theming/theming.scss +++ b/libs/theme-core/src/theming/theming.scss @@ -139,6 +139,13 @@ } } +@mixin focusable-within() { + html.keyboard-mode &:has(:focus-visible) { + outline: 5px solid var(--focus-color); + z-index: 1; + } +} + @function modifier-hover($color: accent, $palette: bg) { @return rgba(var(--color-#{$palette}-#{$color}-rgb), var(--hover-opacity)); } diff --git a/libs/theme-default/src/date-picker/date-picker.scss b/libs/theme-default/src/date-picker/date-picker.scss index 349f2b01..63d56b90 100644 --- a/libs/theme-default/src/date-picker/date-picker.scss +++ b/libs/theme-default/src/date-picker/date-picker.scss @@ -4,12 +4,17 @@ @mixin theme() { xui-date-picker { - display: flex; + width: fit-content; } .x-datepicker { + @include core.focusable(); @include input-base.input($border-radius); + .x-icon { + font-size: 20px; + } + &-popup { width: fit-content; display: flex; diff --git a/libs/theme-default/src/input/input.scss b/libs/theme-default/src/input/input.scss index 4e649728..b4d543ec 100644 --- a/libs/theme-default/src/input/input.scss +++ b/libs/theme-default/src/input/input.scss @@ -1,8 +1,10 @@ @use 'variables' as *; @use '../mixins/input-base'; +@use '@xui/theme-core' as core; @mixin theme() { .x-input { + @include core.focusable-within(); @include input-base.input($border-radius); @include input-base.error(); } diff --git a/libs/theme-default/src/mixins/input-base.scss b/libs/theme-default/src/mixins/input-base.scss index ad89033d..cdbaffe1 100644 --- a/libs/theme-default/src/mixins/input-base.scss +++ b/libs/theme-default/src/mixins/input-base.scss @@ -8,7 +8,6 @@ box-sizing: border-box; @include core.border-radius($border-radius); - @include core.focusable(); input { margin: 0; diff --git a/libs/theme-default/src/select/select.scss b/libs/theme-default/src/select/select.scss index 7e40e7d6..022bba1e 100644 --- a/libs/theme-default/src/select/select.scss +++ b/libs/theme-default/src/select/select.scss @@ -47,6 +47,10 @@ border: 1px solid var(--color-bg-tertiary); border-top: none; + &:focus-visible { + outline: none; + } + @include core.border-bottom-radius-only($border-radius); @include core.fill(primary); } diff --git a/libs/theme-default/src/textarea/textarea.scss b/libs/theme-default/src/textarea/textarea.scss index 0816e752..e71aaa91 100644 --- a/libs/theme-default/src/textarea/textarea.scss +++ b/libs/theme-default/src/textarea/textarea.scss @@ -8,7 +8,7 @@ position: relative; @include core.border-radius($border-radius); - @include core.focusable(); + @include core.focusable-within(); textarea { width: 100%; diff --git a/libs/xui/src/button/button.module.ts b/libs/xui/src/button/button.module.ts index 57288a8d..e1f8bc7d 100644 --- a/libs/xui/src/button/button.module.ts +++ b/libs/xui/src/button/button.module.ts @@ -2,9 +2,10 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { XuiButton } from './button'; import { XuiButtonGroup } from './button-group'; +import { XuiFocusModule } from '../utils/focus.service'; @NgModule({ - imports: [CommonModule], + imports: [CommonModule, XuiFocusModule], declarations: [XuiButton, XuiButtonGroup], exports: [XuiButton, XuiButtonGroup] }) diff --git a/libs/xui/src/checkbox/checkbox.ts b/libs/xui/src/checkbox/checkbox.ts index 9ae77b5a..2ed3d826 100644 --- a/libs/xui/src/checkbox/checkbox.ts +++ b/libs/xui/src/checkbox/checkbox.ts @@ -4,10 +4,11 @@ import { convertToBoolean } from '../utils'; import { CheckboxColor } from './checkbox.types'; import { CHECKBOX_MODULE, XuiConfigService } from '../config'; import { CommonModule } from '@angular/common'; +import { XuiFocusModule } from '../utils/focus.service'; @Component({ standalone: true, - imports: [CommonModule], + imports: [CommonModule, XuiFocusModule], selector: 'xui-checkbox', changeDetection: ChangeDetectionStrategy.OnPush, template: `
diff --git a/libs/xui/src/context-menu/context-menu.module.ts b/libs/xui/src/context-menu/context-menu.module.ts index b1f14883..2f6831e4 100644 --- a/libs/xui/src/context-menu/context-menu.module.ts +++ b/libs/xui/src/context-menu/context-menu.module.ts @@ -5,10 +5,11 @@ import { CdkMenuModule } from '@angular/cdk/menu'; import { A11yModule } from '@angular/cdk/a11y'; import { XuiMenuTriggerFor } from './menu-trigger-for'; import { XuiContextMenuTriggerFor } from './context-menu-trigger-for'; +import { XuiFocusModule } from '../utils/focus.service'; @NgModule({ declarations: [XuiContextMenu, XuiMenuTriggerFor, XuiContextMenuTriggerFor], - imports: [CommonModule, CdkMenuModule, A11yModule], + imports: [CommonModule, CdkMenuModule, A11yModule, XuiFocusModule], exports: [XuiContextMenu, CdkMenuModule, XuiMenuTriggerFor, XuiContextMenuTriggerFor] }) export class XuiContextMenuModule {} diff --git a/libs/xui/src/date-picker/date-picker.html b/libs/xui/src/date-picker/date-picker.html index 34c2d5f6..476e2ef7 100644 --- a/libs/xui/src/date-picker/date-picker.html +++ b/libs/xui/src/date-picker/date-picker.html @@ -1,5 +1,5 @@
- +
-
+
@for (item of items(); track item.value) { {{ item.label | translate }} } diff --git a/libs/xui/src/select/select.module.ts b/libs/xui/src/select/select.module.ts index 30e27ee6..b3af5fde 100644 --- a/libs/xui/src/select/select.module.ts +++ b/libs/xui/src/select/select.module.ts @@ -8,9 +8,19 @@ import { XuiOption } from './option'; import { XuiIcon } from '../icon'; import { XuiDecagram } from '../decagram'; import { A11yModule } from '@angular/cdk/a11y'; +import { XuiFocusModule } from '../utils/focus.service'; @NgModule({ - imports: [CommonModule, FormsModule, A11yModule, OverlayModule, XuiIcon, XuiDecagram, TranslateModule.forChild()], + imports: [ + CommonModule, + FormsModule, + A11yModule, + OverlayModule, + XuiIcon, + XuiDecagram, + TranslateModule.forChild(), + XuiFocusModule + ], declarations: [XuiSelect, XuiOption], exports: [XuiSelect, XuiOption] }) diff --git a/libs/xui/src/settings/settings.module.ts b/libs/xui/src/settings/settings.module.ts index 0652d364..d2344714 100644 --- a/libs/xui/src/settings/settings.module.ts +++ b/libs/xui/src/settings/settings.module.ts @@ -7,10 +7,19 @@ import { XuiButtonModule } from '../button'; import { A11yModule } from '@angular/cdk/a11y'; import { XuiIcon } from '../icon'; import { SaveResetSnackbar } from './settings-snackbar'; +import { XuiFocusModule } from '../utils/focus.service'; @NgModule({ declarations: [XuiSettings, SaveResetSnackbar], - imports: [CommonModule, XuiIcon, XuiButtonModule, PortalModule, A11yModule, TranslateModule.forChild()], + imports: [ + CommonModule, + XuiIcon, + XuiButtonModule, + PortalModule, + A11yModule, + TranslateModule.forChild(), + XuiFocusModule + ], exports: [XuiSettings] }) export class XuiSettingsModule {} diff --git a/libs/xui/src/slider/slider.ts b/libs/xui/src/slider/slider.ts index 80e14165..e83469bd 100644 --- a/libs/xui/src/slider/slider.ts +++ b/libs/xui/src/slider/slider.ts @@ -17,10 +17,11 @@ import { SliderColor, SliderMark } from './slider.types'; import { CommonModule } from '@angular/common'; import { XuiTooltip, XuiTooltipModule } from '../tooltip'; import { DragDropModule } from '@angular/cdk/drag-drop'; +import { XuiFocusModule } from '../utils/focus.service'; @Component({ standalone: true, - imports: [CommonModule, FormsModule, XuiTooltipModule, DragDropModule], + imports: [CommonModule, FormsModule, XuiTooltipModule, DragDropModule, XuiFocusModule], selector: 'xui-slider', changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: 'slider.html', diff --git a/libs/xui/src/snack-bar/snack-bar.module.ts b/libs/xui/src/snack-bar/snack-bar.module.ts index ac216ab9..3b33bc02 100644 --- a/libs/xui/src/snack-bar/snack-bar.module.ts +++ b/libs/xui/src/snack-bar/snack-bar.module.ts @@ -6,9 +6,18 @@ import { SimpleSnackBar } from './simple-snack-bar'; import { XuiButtonModule } from '../button'; import { OverlayModule } from '@angular/cdk/overlay'; import { XuiSnackBar } from './snack-bar'; +import { XuiFocusModule } from '../utils/focus.service'; @NgModule({ - imports: [CommonModule, XuiButtonModule, OverlayModule, PortalModule, SimpleSnackBar, TranslateModule.forChild()], + imports: [ + CommonModule, + XuiButtonModule, + OverlayModule, + PortalModule, + SimpleSnackBar, + TranslateModule.forChild(), + XuiFocusModule + ], providers: [XuiSnackBar] }) export class XuiSnackbarModule {} diff --git a/libs/xui/src/switch/switch.ts b/libs/xui/src/switch/switch.ts index efc36005..3d978455 100644 --- a/libs/xui/src/switch/switch.ts +++ b/libs/xui/src/switch/switch.ts @@ -4,10 +4,11 @@ import { ControlValueAccessor, NgControl } from '@angular/forms'; import { SwitchColor } from './switch.types'; import { CommonModule } from '@angular/common'; import { XuiIcon } from '../icon'; +import { XuiFocusModule } from '../utils/focus.service'; @Component({ standalone: true, - imports: [CommonModule, XuiIcon], + imports: [CommonModule, XuiIcon, XuiFocusModule], selector: 'xui-switch', changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: 'switch.html', diff --git a/libs/xui/src/tabs/tab.module.ts b/libs/xui/src/tabs/tab.module.ts index 8512f70c..aa18d0b8 100644 --- a/libs/xui/src/tabs/tab.module.ts +++ b/libs/xui/src/tabs/tab.module.ts @@ -3,10 +3,11 @@ import { CommonModule } from '@angular/common'; import { XuiTab } from './tab'; import { XuiTabGroup } from './tab-group'; import { TranslateModule } from '@ngx-translate/core'; +import { XuiFocusModule } from '../utils/focus.service'; @NgModule({ declarations: [XuiTab, XuiTabGroup], - imports: [CommonModule, TranslateModule.forChild()], + imports: [CommonModule, TranslateModule.forChild(), XuiFocusModule], exports: [XuiTab, XuiTabGroup] }) export class XuiTabModule {} diff --git a/libs/xui/src/textarea/textarea.html b/libs/xui/src/textarea/textarea.html index bef230bc..c208b709 100644 --- a/libs/xui/src/textarea/textarea.html +++ b/libs/xui/src/textarea/textarea.html @@ -1,4 +1,4 @@ -
+
{{ _worldCount() }}
diff --git a/libs/xui/src/textarea/textarea.ts b/libs/xui/src/textarea/textarea.ts index 93aa9c65..48557f65 100644 --- a/libs/xui/src/textarea/textarea.ts +++ b/libs/xui/src/textarea/textarea.ts @@ -4,10 +4,11 @@ import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { convertToBoolean } from '../utils'; import { TextareaColor, TextareaSize } from './textarea.types'; import { CommonModule } from '@angular/common'; +import { XuiFocusModule } from '../utils/focus.service'; @Component({ standalone: true, - imports: [CommonModule, FormsModule, TranslateModule], + imports: [CommonModule, FormsModule, TranslateModule, XuiFocusModule], selector: 'xui-textarea', changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: 'textarea.html', diff --git a/libs/xui/src/toggle/toggle.ts b/libs/xui/src/toggle/toggle.ts index 981a41c9..a7ca81ff 100644 --- a/libs/xui/src/toggle/toggle.ts +++ b/libs/xui/src/toggle/toggle.ts @@ -4,10 +4,11 @@ import { convertToBoolean } from '../utils'; import { ToggleColor } from './toggle.types'; import { CommonModule } from '@angular/common'; import { XuiIcon } from '../icon'; +import { XuiFocusModule } from '../utils/focus.service'; @Component({ standalone: true, - imports: [CommonModule, XuiIcon], + imports: [CommonModule, XuiIcon, XuiFocusModule], selector: 'xui-toggle', changeDetection: ChangeDetectionStrategy.OnPush, template: `
diff --git a/libs/xui/src/tooltip/tooltip.module.ts b/libs/xui/src/tooltip/tooltip.module.ts index a1665d74..6a22f300 100644 --- a/libs/xui/src/tooltip/tooltip.module.ts +++ b/libs/xui/src/tooltip/tooltip.module.ts @@ -4,10 +4,11 @@ import { TranslateModule } from '@ngx-translate/core'; import { XuiTooltip } from './tooltip.directive'; import { Tooltip } from './tooltip'; import { OverlayModule } from '@angular/cdk/overlay'; +import { XuiFocusModule } from '../utils/focus.service'; @NgModule({ declarations: [XuiTooltip, Tooltip], - imports: [CommonModule, OverlayModule, TranslateModule.forChild()], + imports: [CommonModule, OverlayModule, TranslateModule.forChild(), XuiFocusModule], exports: [XuiTooltip] }) export class XuiTooltipModule {} diff --git a/libs/xui/src/utils/focus.service.ts b/libs/xui/src/utils/focus.service.ts new file mode 100644 index 00000000..3cf90e8f --- /dev/null +++ b/libs/xui/src/utils/focus.service.ts @@ -0,0 +1,32 @@ +import { Inject, Injectable, NgModule } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; + +@Injectable({ providedIn: 'root' }) +export class XuiFocusService { + constructor(@Inject(DOCUMENT) doc: Document) { + doc.documentElement.addEventListener('mousedown', function () { + doc.documentElement.classList.add('mouse-mode'); + doc.documentElement.classList.remove('keyboard-mode'); + }); + + doc.documentElement.addEventListener('keydown', function (event: KeyboardEvent) { + if (event.code == 'Tab') { + doc.documentElement.classList.add('keyboard-mode'); + doc.documentElement.classList.remove('mouse-mode'); + } + }); + + window.addEventListener('focus', function () { + doc.documentElement.classList.add('app-focused'); + }); + + window.addEventListener('blur', function () { + doc.documentElement.classList.remove('app-focused'); + }); + } +} + +@NgModule({ providers: [XuiFocusService] }) +export class XuiFocusModule { + constructor(_: XuiFocusService) {} +}