@hmcts/opal-frontend-common 0.0.110 → 0.0.112

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,61 @@
1
+ import { DOCUMENT } from "@angular/common";
2
+ import * as i0 from "@angular/core";
3
+ import { ChangeDetectionStrategy, Component, Input, inject } from "@angular/core";
4
+ import { UtilsService } from "@hmcts/opal-frontend-common/services/utils-service";
5
+ var GovukSkipLinkComponent = class GovukSkipLinkComponent {
6
+ document = inject(DOCUMENT);
7
+ utilsService = inject(UtilsService);
8
+ targetId = "main-content";
9
+ linkText = "Skip to main content";
10
+ get href() {
11
+ return `${this.document.location?.pathname ?? ""}#${this.targetId}`;
12
+ }
13
+ onSkipLink(event) {
14
+ event.preventDefault();
15
+ this.utilsService.focusElementById(this.targetId);
16
+ }
17
+ static ɵfac = i0.ɵɵngDeclareFactory({
18
+ minVersion: "12.0.0",
19
+ version: "21.2.25",
20
+ ngImport: i0,
21
+ type: GovukSkipLinkComponent,
22
+ deps: [],
23
+ target: i0.ɵɵFactoryTarget.Component
24
+ });
25
+ static ɵcmp = i0.ɵɵngDeclareComponent({
26
+ minVersion: "14.0.0",
27
+ version: "21.2.25",
28
+ type: GovukSkipLinkComponent,
29
+ isStandalone: true,
30
+ selector: "opal-lib-govuk-skip-link",
31
+ inputs: {
32
+ targetId: "targetId",
33
+ linkText: "linkText"
34
+ },
35
+ ngImport: i0,
36
+ template: "<a class=\"govuk-skip-link\" [href]=\"href\" data-module=\"govuk-skip-link\" (click)=\"onSkipLink($event)\">{{ linkText }}</a>\n",
37
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
38
+ });
39
+ };
40
+ i0.ɵɵngDeclareClassMetadata({
41
+ minVersion: "12.0.0",
42
+ version: "21.2.25",
43
+ ngImport: i0,
44
+ type: GovukSkipLinkComponent,
45
+ decorators: [{
46
+ type: Component,
47
+ args: [{
48
+ selector: "opal-lib-govuk-skip-link",
49
+ imports: [],
50
+ changeDetection: ChangeDetectionStrategy.OnPush,
51
+ template: "<a class=\"govuk-skip-link\" [href]=\"href\" data-module=\"govuk-skip-link\" (click)=\"onSkipLink($event)\">{{ linkText }}</a>\n"
52
+ }]
53
+ }],
54
+ propDecorators: {
55
+ targetId: [{ type: Input }],
56
+ linkText: [{ type: Input }]
57
+ }
58
+ });
59
+ export { GovukSkipLinkComponent };
60
+
61
+ //# sourceMappingURL=hmcts-opal-frontend-common-components-govuk-govuk-skip-link.mjs.map
@@ -331,7 +331,7 @@ var MojSortableTableComponent = class MojSortableTableComponent {
331
331
  selector: "opal-lib-moj-sortable-table",
332
332
  inputs: { tableClasses: "tableClasses" },
333
333
  ngImport: i0,
334
- template: "<table class=\"govuk-table {{ tableClasses }}\" data-module=\"moj-sortable-table\">\n <caption class=\"govuk-table__caption\">\n <ng-content select=\"[caption]\"></ng-content>\n </caption>\n <thead class=\"govuk-table__head\">\n <tr class=\"govuk-table__row\">\n <ng-content select=\"[head]\"></ng-content>\n </tr>\n </thead>\n <tbody class=\"govuk-table__body\">\n <ng-content select=\"[row]\"></ng-content>\n <ng-content select=\"[results]\"></ng-content>\n </tbody>\n</table>\n<ng-content select=\"[status]\"></ng-content>\n",
334
+ template: "<table class=\"govuk-table {{ tableClasses }}\" data-module=\"moj-sortable-table\">\n <caption class=\"govuk-table__caption\">\n <ng-content select=\"[caption]\">\n <span class=\"govuk-visually-hidden\">Column headers with buttons are sortable</span>\n </ng-content>\n </caption>\n <thead class=\"govuk-table__head\">\n <tr class=\"govuk-table__row\">\n <ng-content select=\"[head]\"></ng-content>\n </tr>\n </thead>\n <tbody class=\"govuk-table__body\">\n <ng-content select=\"[row]\"></ng-content>\n <ng-content select=\"[results]\"></ng-content>\n </tbody>\n</table>\n<ng-content select=\"[status]\"></ng-content>\n",
335
335
  changeDetection: i0.ChangeDetectionStrategy.OnPush
336
336
  });
337
337
  };
@@ -345,7 +345,7 @@ i0.ɵɵngDeclareClassMetadata({
345
345
  args: [{
346
346
  selector: "opal-lib-moj-sortable-table",
347
347
  changeDetection: ChangeDetectionStrategy.OnPush,
348
- template: "<table class=\"govuk-table {{ tableClasses }}\" data-module=\"moj-sortable-table\">\n <caption class=\"govuk-table__caption\">\n <ng-content select=\"[caption]\"></ng-content>\n </caption>\n <thead class=\"govuk-table__head\">\n <tr class=\"govuk-table__row\">\n <ng-content select=\"[head]\"></ng-content>\n </tr>\n </thead>\n <tbody class=\"govuk-table__body\">\n <ng-content select=\"[row]\"></ng-content>\n <ng-content select=\"[results]\"></ng-content>\n </tbody>\n</table>\n<ng-content select=\"[status]\"></ng-content>\n"
348
+ template: "<table class=\"govuk-table {{ tableClasses }}\" data-module=\"moj-sortable-table\">\n <caption class=\"govuk-table__caption\">\n <ng-content select=\"[caption]\">\n <span class=\"govuk-visually-hidden\">Column headers with buttons are sortable</span>\n </ng-content>\n </caption>\n <thead class=\"govuk-table__head\">\n <tr class=\"govuk-table__row\">\n <ng-content select=\"[head]\"></ng-content>\n </tr>\n </thead>\n <tbody class=\"govuk-table__body\">\n <ng-content select=\"[row]\"></ng-content>\n <ng-content select=\"[results]\"></ng-content>\n </tbody>\n</table>\n<ng-content select=\"[status]\"></ng-content>\n"
349
349
  }]
350
350
  }],
351
351
  propDecorators: { tableClasses: [{
@@ -1,3 +1,4 @@
1
+ import { DOCUMENT } from "@angular/common";
1
2
  import * as i0 from "@angular/core";
2
3
  import { Directive, Input, inject } from "@angular/core";
3
4
  import { Subject, takeUntil } from "rxjs";
@@ -5,15 +6,34 @@ import { UtilsService } from "@hmcts/opal-frontend-common/services/utils-service
5
6
  var CapitalisationDirective = class CapitalisationDirective {
6
7
  destroy$ = new Subject();
7
8
  utilsService = inject(UtilsService);
9
+ document = inject(DOCUMENT);
8
10
  control;
11
+ captureSelection(value) {
12
+ const activeElement = this.document.activeElement;
13
+ if (activeElement?.tagName !== "INPUT" && activeElement?.tagName !== "TEXTAREA") return null;
14
+ const input = activeElement;
15
+ const { selectionStart, selectionEnd, selectionDirection } = input;
16
+ if (input.value !== value || typeof selectionStart !== "number" || typeof selectionEnd !== "number") return null;
17
+ return {
18
+ input,
19
+ start: this.utilsService.upperCaseAllLetters(value.slice(0, selectionStart)).length,
20
+ end: this.utilsService.upperCaseAllLetters(value.slice(0, selectionEnd)).length,
21
+ direction: selectionDirection ?? "none"
22
+ };
23
+ }
24
+ capitaliseControlValue(value) {
25
+ if (typeof value === "string" && value.length > 0) {
26
+ const upper = this.utilsService.upperCaseAllLetters(value);
27
+ if (value !== upper) {
28
+ const selection = this.captureSelection(value);
29
+ this.control.setValue(upper, { emitEvent: false });
30
+ if (selection?.input === this.document.activeElement && selection.input.value === upper) selection.input.setSelectionRange(selection.start, selection.end, selection.direction);
31
+ }
32
+ }
33
+ }
9
34
  ngOnInit() {
10
35
  if (!this.control) return;
11
- this.control.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => {
12
- if (typeof value === "string" && value.length > 0) {
13
- const upper = this.utilsService.upperCaseAllLetters(value);
14
- if (value !== upper) this.control.setValue(upper, { emitEvent: false });
15
- }
16
- });
36
+ this.control.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => this.capitaliseControlValue(value));
17
37
  }
18
38
  ngOnDestroy() {
19
39
  this.destroy$.next();
@@ -30,15 +30,17 @@ var UtilsService = class UtilsService {
30
30
  scrollToTop() {
31
31
  this.viewportScroller.scrollToPosition([0, 0]);
32
32
  }
33
- focusAndScrollToTop() {
34
- const mainContent = this.document.getElementById("main-content");
35
- if (mainContent) {
36
- if (!mainContent.hasAttribute("tabindex")) {
37
- mainContent.setAttribute("tabindex", "-1");
38
- mainContent.addEventListener("blur", () => mainContent.removeAttribute("tabindex"), { once: true });
39
- }
40
- mainContent.focus({ preventScroll: true });
33
+ focusElementById(elementId, options) {
34
+ const element = this.document.getElementById(elementId);
35
+ if (!element) return;
36
+ if (!element.hasAttribute("tabindex")) {
37
+ element.setAttribute("tabindex", "-1");
38
+ element.addEventListener("blur", () => element.removeAttribute("tabindex"), { once: true });
41
39
  }
40
+ element.focus(options);
41
+ }
42
+ focusAndScrollToTop() {
43
+ this.focusElementById("main-content", { preventScroll: true });
42
44
  this.scrollToTop();
43
45
  }
44
46
  checkFormValues(form) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hmcts/opal-frontend-common",
3
- "version": "0.0.110",
3
+ "version": "0.0.112",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -416,6 +416,11 @@
416
416
  "types": "./types/hmcts-opal-frontend-common-components-govuk-govuk-select-interfaces.d.ts",
417
417
  "default": "./fesm2022/hmcts-opal-frontend-common-components-govuk-govuk-select-interfaces.mjs"
418
418
  },
419
+ "./components/govuk/govuk-skip-link": {
420
+ "import": "./fesm2022/hmcts-opal-frontend-common-components-govuk-govuk-skip-link.mjs",
421
+ "types": "./types/hmcts-opal-frontend-common-components-govuk-govuk-skip-link.d.ts",
422
+ "default": "./fesm2022/hmcts-opal-frontend-common-components-govuk-govuk-skip-link.mjs"
423
+ },
419
424
  "./components/govuk/govuk-summary-card-list": {
420
425
  "import": "./fesm2022/hmcts-opal-frontend-common-components-govuk-govuk-summary-card-list.mjs",
421
426
  "types": "./types/hmcts-opal-frontend-common-components-govuk-govuk-summary-card-list.d.ts",
@@ -0,0 +1,20 @@
1
+ import * as i0 from "@angular/core";
2
+ export declare class GovukSkipLinkComponent {
3
+ private readonly document;
4
+ private readonly utilsService;
5
+ targetId: string;
6
+ linkText: string;
7
+ get href(): string;
8
+ onSkipLink(event: Event): void;
9
+ static ɵfac: i0.ɵɵFactoryDeclaration<GovukSkipLinkComponent, never>;
10
+ static ɵcmp: i0.ɵɵComponentDeclaration<GovukSkipLinkComponent, "opal-lib-govuk-skip-link", never, {
11
+ "targetId": {
12
+ "alias": "targetId";
13
+ "required": false;
14
+ };
15
+ "linkText": {
16
+ "alias": "linkText";
17
+ "required": false;
18
+ };
19
+ }, {}, never, never, true, never>;
20
+ }
@@ -4,7 +4,23 @@ import { AbstractControl } from "@angular/forms";
4
4
  export declare class CapitalisationDirective implements OnInit, OnDestroy {
5
5
  private readonly destroy$;
6
6
  private readonly utilsService;
7
+ private readonly document;
7
8
  control: AbstractControl;
9
+ /**
10
+ * Captures the focused input or textarea selection before its value is uppercased.
11
+ * Maps offsets through uppercase prefixes to account for expanding characters, such as ß becoming SS.
12
+ * Defaults missing selection direction to 'none' for document implementations with incomplete selection support.
13
+ *
14
+ * @param value The current control value, which must match the focused input's value.
15
+ * @returns The input and its mapped selection, or null when the value differs or selection is unsupported.
16
+ */
17
+ private captureSelection;
18
+ /**
19
+ * Uppercases a changed control value while preserving the focused input's selection.
20
+ *
21
+ * @param value The latest value emitted by the form control.
22
+ */
23
+ private capitaliseControlValue;
8
24
  ngOnInit(): void;
9
25
  ngOnDestroy(): void;
10
26
  static ɵfac: i0.ɵɵFactoryDeclaration<CapitalisationDirective, never>;
@@ -39,6 +39,14 @@ export declare class UtilsService {
39
39
  * Utilizes the `viewportScroller` service to scroll to the position [0, 0].
40
40
  */
41
41
  scrollToTop(): void;
42
+ /**
43
+ * Moves focus to an element by ID.
44
+ * A temporary tabindex allows a normally non-focusable element to receive programmatic focus.
45
+ *
46
+ * @param elementId - The ID of the element to focus.
47
+ * @param options - Options passed to the element's focus method.
48
+ */
49
+ focusElementById(elementId: string, options?: FocusOptions): void;
42
50
  /**
43
51
  * Moves focus to the main content landmark and scrolls the viewport to the top of the page.
44
52
  * A temporary tabindex allows the normally non-focusable landmark to receive programmatic focus.