@ministryofjustice/hmpps-connect-dps-shared-items 5.1.0 → 5.2.0

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.
Files changed (26) hide show
  1. package/dist/assets/dps/components/address-autosuggest/_address-autosuggest.scss +7 -0
  2. package/dist/assets/dps/components/address-autosuggest/abortable-fetch.d.ts +26 -0
  3. package/dist/assets/dps/components/address-autosuggest/abortable-fetch.js +20 -8
  4. package/dist/assets/dps/components/address-autosuggest/abortable-fetch.ts +76 -0
  5. package/dist/assets/dps/components/address-autosuggest/autosuggest-address.d.ts +18 -0
  6. package/dist/assets/dps/components/address-autosuggest/autosuggest-address.js +27 -13
  7. package/dist/assets/dps/components/address-autosuggest/autosuggest-address.ts +108 -0
  8. package/dist/assets/dps/components/address-autosuggest/autosuggest-ui.d.ts +82 -0
  9. package/dist/assets/dps/components/address-autosuggest/autosuggest-ui.js +83 -53
  10. package/dist/assets/dps/components/address-autosuggest/autosuggest-ui.ts +459 -0
  11. package/dist/assets/dps/components/address-autosuggest/index.d.ts +1 -0
  12. package/dist/assets/dps/components/address-autosuggest/index.js +4 -4
  13. package/dist/assets/dps/components/address-autosuggest/index.ts +9 -0
  14. package/dist/assets/dps/components/alert-flag/_alert-flag.scss +2 -1
  15. package/dist/assets/dps/components/modal/index.d.ts +34 -0
  16. package/dist/assets/dps/components/modal/macro.njk +2 -1
  17. package/dist/assets/dps/components/webcam-capture/_webcam-capture.scss +2 -2
  18. package/dist/assets/dps/components/webcam-capture/index.d.ts +1 -0
  19. package/dist/assets/dps/components/webcam-capture/index.js +14 -8
  20. package/dist/assets/dps/components/webcam-capture/index.ts +165 -0
  21. package/dist/assets/js/all.d.ts +1 -0
  22. package/dist/assets/js/all.ts +10 -0
  23. package/dist/assets/scss/all.css +66 -16
  24. package/dist/index.d.ts +10 -1
  25. package/dist/types/public/addressAutosuggest/addressAutosuggestResponse.d.ts +9 -0
  26. package/package.json +4 -4
@@ -0,0 +1,165 @@
1
+ function hmppsWebcamCapture(component: HTMLElement): Promise<void> {
2
+ const video = document.getElementById('hmpps-webcam__webcam') as HTMLVideoElement
3
+ const captureBtn = document.getElementById('hmpps-webcam__captureImageButton') as HTMLButtonElement
4
+ const clearImageBtn = document.getElementById('hmpps-webcam__clearImageButton') as HTMLButtonElement
5
+ const snapshot = document.getElementById('hmpps-webcam__snapshot') as HTMLImageElement
6
+ const webcamImageFileInput = document.getElementById('hmpps-webcam__webcam-image-input') as HTMLInputElement
7
+ const webcamSelect = document.getElementById('hmpps-webcam__select-webcam') as HTMLSelectElement
8
+ const webcamSelectFormGroup = document.getElementById('hmpps-webcam__webcam-select-form-group') as HTMLDivElement
9
+ const webcamSubmit = document.getElementById('hmpps-webcam__webcam-submit') as HTMLButtonElement
10
+ const webcamPlaceholder = document.getElementById('hmpps-webcam__webcam-placeholder') as HTMLDivElement
11
+ const fileName = component.dataset.fileName ?? 'webcam-capture'
12
+ const enableReporting = component.dataset.reportErrors === 'true'
13
+
14
+ // Permissions
15
+ const permissionRequested = document.getElementById('hmpps-webcam__permission-requested') as HTMLParagraphElement
16
+ const permissionGranted = document.getElementById('hmpps-webcam__permission-granted') as HTMLDivElement
17
+
18
+ // Preview elements
19
+ const photoPreviewContainer = document.getElementById('hmpps-webcam__photo-preview-container') as HTMLDivElement
20
+ const photoCaptureContainer = document.getElementById('hmpps-webcam__photo-capture-container') as HTMLDivElement
21
+ const photoCaptureErrorContainer = document.getElementById(
22
+ 'hmpps-webcam__photo-capture-container__error',
23
+ ) as HTMLDivElement
24
+
25
+ const mimetype = 'image/jpeg'
26
+
27
+ let stream: MediaStream | null = null
28
+
29
+ function updateWebcamList(activeDeviceId: string): void {
30
+ webcamSelect.querySelectorAll('option').forEach(option => {
31
+ if (option.value === activeDeviceId && option.label.indexOf('- Active') === -1) {
32
+ // eslint-disable-next-line no-param-reassign
33
+ option.label = `${option.label} - Active`
34
+ } else if (option.value !== activeDeviceId && option.label.indexOf('- Active') > -1) {
35
+ // eslint-disable-next-line no-param-reassign
36
+ option.label = option.label.replace(' - Active', '')
37
+ }
38
+ })
39
+ }
40
+
41
+ async function getWebcamList(): Promise<void> {
42
+ // get camera permissions
43
+ try {
44
+ permissionRequested.style.display = 'block'
45
+ await navigator.mediaDevices.getUserMedia({ video: true, audio: false })
46
+
47
+ navigator.mediaDevices.enumerateDevices().then(devices => {
48
+ const videoDevices = devices.filter(device => device.kind === 'videoinput')
49
+
50
+ videoDevices.forEach((device, index) => {
51
+ const option = document.createElement('option')
52
+ option.value = device.deviceId
53
+ option.text = device.label || `Camera ${index + 1}`
54
+ webcamSelect.appendChild(option)
55
+ })
56
+
57
+ if (videoDevices.length < 2) {
58
+ webcamSelectFormGroup.style.display = 'none'
59
+ }
60
+
61
+ if (videoDevices.length > 0) {
62
+ startWebcam(videoDevices[0].deviceId)
63
+ }
64
+ })
65
+ } catch (e) {
66
+ photoCaptureContainer.style.display = 'none'
67
+ photoCaptureErrorContainer.style.display = 'block'
68
+ if (enableReporting) {
69
+ // @ts-expect-error error type is not known
70
+ const error = 'name' in e ? e.name : e.message
71
+ // eslint-disable-next-line no-restricted-globals
72
+ await fetch(`/api/report-error?pageUrl=${encodeURIComponent(location.href)}&error=${error}`, {
73
+ method: 'GET',
74
+ })
75
+ }
76
+ }
77
+ }
78
+
79
+ function stopWebcam(): void {
80
+ if (stream) {
81
+ stream.getTracks().forEach(track => track.stop())
82
+ }
83
+ }
84
+
85
+ async function startWebcam(deviceId: string): Promise<void> {
86
+ try {
87
+ stopWebcam()
88
+ stream = await navigator.mediaDevices.getUserMedia({
89
+ video: {
90
+ deviceId: { exact: deviceId },
91
+ },
92
+ })
93
+ video.srcObject = stream
94
+ permissionRequested.style.display = 'none'
95
+ permissionGranted.style.display = 'block'
96
+ webcamPlaceholder.style.display = 'none'
97
+ video.style.display = 'block'
98
+ captureBtn.disabled = false
99
+ updateWebcamList(deviceId)
100
+ } catch (error) {
101
+ permissionRequested.style.display = 'none'
102
+ photoCaptureContainer.style.display = 'none'
103
+ photoCaptureErrorContainer.style.display = 'block'
104
+ // eslint-disable-next-line no-console
105
+ console.error('Webcam error:', error)
106
+ }
107
+ }
108
+
109
+ webcamSelect.addEventListener('change', (): void => {
110
+ startWebcam(webcamSelect.value)
111
+ })
112
+
113
+ clearImageBtn.addEventListener('click', (): void => {
114
+ snapshot.src = ''
115
+
116
+ photoPreviewContainer.style.display = 'none'
117
+ photoCaptureContainer.style.display = 'block'
118
+ webcamSubmit.disabled = true
119
+ })
120
+
121
+ captureBtn.addEventListener('click', (): void => {
122
+ const canvas = document.createElement('canvas')
123
+ canvas.width = video.videoWidth
124
+ canvas.height = video.videoHeight
125
+ const ctx = canvas.getContext('2d')!
126
+
127
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
128
+
129
+ snapshot.src = canvas.toDataURL(mimetype)
130
+
131
+ canvas.toBlob(
132
+ blob => {
133
+ if (!blob) return
134
+
135
+ const file = new File([blob], `${fileName}.jpg`, { type: mimetype })
136
+
137
+ const dataTransfer = new DataTransfer()
138
+ dataTransfer.items.add(file)
139
+ webcamImageFileInput.files = dataTransfer.files
140
+ },
141
+ mimetype,
142
+ 0.92,
143
+ )
144
+
145
+ photoPreviewContainer.style.display = 'block'
146
+ photoCaptureContainer.style.display = 'none'
147
+ webcamSubmit.disabled = false
148
+ })
149
+
150
+ function pageInit(): Promise<void> {
151
+ photoPreviewContainer.style.display = 'none'
152
+ photoCaptureContainer.style.display = 'block'
153
+ return getWebcamList()
154
+ }
155
+
156
+ return pageInit()
157
+ }
158
+
159
+ // eslint-disable-next-line import/prefer-default-export
160
+ export function init(): void {
161
+ const webcamContainer = document.getElementById('hmpps-webcam-capture-component')
162
+ if (webcamContainer) {
163
+ hmppsWebcamCapture(webcamContainer)
164
+ }
165
+ }
@@ -0,0 +1 @@
1
+ export declare function initAll(): void;
@@ -0,0 +1,10 @@
1
+ import * as addressAutosuggest from '../dps/components/address-autosuggest'
2
+ import { Modal } from '../dps/components/modal'
3
+ import * as webcamCapture from '../dps/components/webcam-capture'
4
+
5
+ // eslint-disable-next-line import/prefer-default-export
6
+ export function initAll() {
7
+ addressAutosuggest.init()
8
+ Modal.init()
9
+ webcamCapture.init()
10
+ }
@@ -4,11 +4,33 @@
4
4
  border: 3px solid;
5
5
  padding: 5px 10px;
6
6
  height: initial;
7
- font-size: 16px;
8
7
  text-align: center;
9
8
  line-height: 1;
10
9
  user-select: none;
11
10
  border-radius: 5px;
11
+ font-family: "GDS Transport", arial, sans-serif;
12
+ -webkit-font-smoothing: antialiased;
13
+ -moz-osx-font-smoothing: grayscale;
14
+ }
15
+ /*! Copyright (c) 2011 by Margaret Calvert & Henrik Kubel. All rights reserved. The font has been customised for exclusive use on gov.uk. This cut is not commercially available. */ /* stylelint-disable-line scss/comment-no-loud */
16
+ @font-face {
17
+ font-family: "GDS Transport";
18
+ font-style: normal;
19
+ font-weight: normal;
20
+ font-display: fallback;
21
+ src: url("/assets/fonts/light-94a07e06a1-v2.woff2") format("woff2"), url("/assets/fonts/light-f591b13f7d-v2.woff") format("woff");
22
+ }
23
+ @font-face {
24
+ font-family: "GDS Transport";
25
+ font-style: normal;
26
+ font-weight: bold;
27
+ font-display: fallback;
28
+ src: url("/assets/fonts/bold-b542beb274-v2.woff2") format("woff2"), url("/assets/fonts/bold-affa96571d-v2.woff") format("woff");
29
+ }
30
+ .dps-alert-status {
31
+ font-weight: 400;
32
+ font-size: 1rem;
33
+ line-height: 1.25;
12
34
  }
13
35
  }
14
36
  @media print {
@@ -497,6 +519,9 @@ a.dps-alert-status--visor:focus {
497
519
  position: relative;
498
520
  max-width: 630px;
499
521
  }
522
+ .hmpps-address-autosuggest .hmpps-display-none {
523
+ display: none;
524
+ }
500
525
  .hmpps-address-autosuggest .govuk-form-group {
501
526
  margin-bottom: 10px;
502
527
  }
@@ -517,6 +542,25 @@ a.dps-alert-status--visor:focus {
517
542
  background: #f3f3f3;
518
543
  border-bottom: 1px solid var(--govuk-border-colour, #cecece);
519
544
  padding: 0.25rem 0.5rem;
545
+ font-family: "GDS Transport", arial, sans-serif;
546
+ -webkit-font-smoothing: antialiased;
547
+ -moz-osx-font-smoothing: grayscale;
548
+ }
549
+ @media print {
550
+ .hmpps-address-autosuggest__results-title {
551
+ font-family: sans-serif;
552
+ }
553
+ }
554
+ .hmpps-address-autosuggest__results-title {
555
+ font-weight: 400;
556
+ font-size: 1.1875rem;
557
+ line-height: 1.3157894737;
558
+ }
559
+ @media print {
560
+ .hmpps-address-autosuggest__results-title {
561
+ font-size: 14pt;
562
+ line-height: 1.15;
563
+ }
520
564
  }
521
565
  .hmpps-address-autosuggest__listbox {
522
566
  background: #ffffff;
@@ -527,6 +571,27 @@ a.dps-alert-status--visor:focus {
527
571
  .hmpps-address-autosuggest__listbox:focus {
528
572
  outline: none;
529
573
  }
574
+ .hmpps-address-autosuggest__listbox {
575
+ font-family: "GDS Transport", arial, sans-serif;
576
+ -webkit-font-smoothing: antialiased;
577
+ -moz-osx-font-smoothing: grayscale;
578
+ }
579
+ @media print {
580
+ .hmpps-address-autosuggest__listbox {
581
+ font-family: sans-serif;
582
+ }
583
+ }
584
+ .hmpps-address-autosuggest__listbox {
585
+ font-weight: 400;
586
+ font-size: 1.1875rem;
587
+ line-height: 1.3157894737;
588
+ }
589
+ @media print {
590
+ .hmpps-address-autosuggest__listbox {
591
+ font-size: 14pt;
592
+ line-height: 1.15;
593
+ }
594
+ }
530
595
  .hmpps-address-autosuggest__option {
531
596
  cursor: pointer;
532
597
  margin: 0;
@@ -620,21 +685,6 @@ a.dps-alert-status--visor:focus {
620
685
  -webkit-font-smoothing: antialiased;
621
686
  -moz-osx-font-smoothing: grayscale;
622
687
  }
623
- /*! Copyright (c) 2011 by Margaret Calvert & Henrik Kubel. All rights reserved. The font has been customised for exclusive use on gov.uk. This cut is not commercially available. */ /* stylelint-disable-line scss/comment-no-loud */
624
- @font-face {
625
- font-family: "GDS Transport";
626
- font-style: normal;
627
- font-weight: normal;
628
- font-display: fallback;
629
- src: url("/assets/fonts/light-94a07e06a1-v2.woff2") format("woff2"), url("/assets/fonts/light-f591b13f7d-v2.woff") format("woff");
630
- }
631
- @font-face {
632
- font-family: "GDS Transport";
633
- font-style: normal;
634
- font-weight: bold;
635
- font-display: fallback;
636
- src: url("/assets/fonts/bold-b542beb274-v2.woff2") format("woff2"), url("/assets/fonts/bold-affa96571d-v2.woff") format("woff");
637
- }
638
688
  @media print {
639
689
  .hmpps-modal__dialogue-box header h1 {
640
690
  font-family: sans-serif;
package/dist/index.d.ts CHANGED
@@ -19,6 +19,15 @@ interface OsAddress {
19
19
  uprn?: number;
20
20
  }
21
21
 
22
+ /** The JSON payload expected by the Address Autosuggest component when loading suggestions */
23
+ type AddressAutosuggestResponse = {
24
+ status: number;
25
+ results: OsAddress[];
26
+ } | {
27
+ status: number;
28
+ error: string;
29
+ };
30
+
22
31
  interface OsPlacesQueryResponse {
23
32
  header: OsPlacesResponseHeader;
24
33
  results: OsPlacesQueryResult[];
@@ -279,4 +288,4 @@ declare const alertFlagLabels: AlertFlagLabel[];
279
288
  declare function getAlertFlagLabelsForAlerts(prisonerAlerts: Alert[]): AlertFlagLabel[];
280
289
 
281
290
  export { AlertType, OsPlacesAddressService, OsPlacesApiClient, alertFlagLabels, getAlertFlagCssClasses, getAlertFlagLabelsForAlerts, getAlertTypeForCode };
282
- export type { Alert, AlertFlagLabel, AlertsServiceAlert, BasicAlert, ConnectDpsComponentLogger, OsAddress, OsPlacesDeliveryPointAddress, OsPlacesLandPropertyIdentifier, OsPlacesQueryResponse, OsPlacesQueryResult, OsPlacesResponseHeader, PrisonApiAlert };
291
+ export type { AddressAutosuggestResponse, Alert, AlertFlagLabel, AlertsServiceAlert, BasicAlert, ConnectDpsComponentLogger, OsAddress, OsPlacesDeliveryPointAddress, OsPlacesLandPropertyIdentifier, OsPlacesQueryResponse, OsPlacesQueryResult, OsPlacesResponseHeader, PrisonApiAlert };
@@ -0,0 +1,9 @@
1
+ import type { OsAddress } from './osAddress';
2
+ /** The JSON payload expected by the Address Autosuggest component when loading suggestions */
3
+ export type AddressAutosuggestResponse = {
4
+ status: number;
5
+ results: OsAddress[];
6
+ } | {
7
+ status: number;
8
+ error: string;
9
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ministryofjustice/hmpps-connect-dps-shared-items",
3
- "version": "5.1.0",
3
+ "version": "5.2.0",
4
4
  "description": "A package to share Connect DPS functionality across applications",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.esm.js",
@@ -26,7 +26,7 @@
26
26
  "precommit:lint": "lint-staged",
27
27
  "precommit:verify": "npm run typecheck && npm test",
28
28
  "setup": "npm ci && hmpps-npm-script-run-allowlist",
29
- "start": "node --experimental-strip-types preview/preview.mts"
29
+ "start": "node --experimental-strip-types preview/server.mts"
30
30
  },
31
31
  "lint-staged": {
32
32
  "*.{ts,css}": [
@@ -67,11 +67,11 @@
67
67
  "lint-staged": "^17.2.0",
68
68
  "nock": "14.0.17",
69
69
  "nunjucks": "3.2.4",
70
- "rollup": "4.62.4",
70
+ "rollup": "4.62.5",
71
71
  "rollup-plugin-copy": "3.5.0",
72
72
  "rollup-plugin-dts": "6.5.1",
73
73
  "rollup-plugin-scss": "4.0.1",
74
- "sass": "1.102.0",
74
+ "sass": "1.103.1",
75
75
  "ts-jest": "29.4.12",
76
76
  "tslib": "^2.8.1",
77
77
  "typescript": "6.0.3"