@fmidev/smartmet-alert-client 4.4.19 → 4.7.0-beta.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 (123) hide show
  1. package/.eslintignore +2 -14
  2. package/.github/workflows/test.yaml +26 -0
  3. package/.nvmrc +1 -0
  4. package/AGENTS.md +26 -0
  5. package/index.html +1 -1
  6. package/package.json +80 -22
  7. package/src/AlertClientVue.vue +160 -0
  8. package/src/App.vue +154 -296
  9. package/src/assets/img/ui/arrow-down.svg +4 -11
  10. package/src/assets/img/ui/arrow-up.svg +4 -11
  11. package/src/assets/img/ui/clear.svg +7 -21
  12. package/src/assets/img/ui/close.svg +4 -15
  13. package/src/assets/img/ui/toggle-selected.svg +5 -6
  14. package/src/assets/img/ui/toggle-unselected.svg +5 -6
  15. package/src/assets/img/warning/cold-weather.svg +3 -6
  16. package/src/assets/img/warning/flood-level-3.svg +4 -7
  17. package/src/assets/img/warning/forest-fire-weather.svg +2 -6
  18. package/src/assets/img/warning/grass-fire-weather.svg +2 -6
  19. package/src/assets/img/warning/hot-weather.svg +3 -6
  20. package/src/assets/img/warning/pedestrian-safety.svg +3 -7
  21. package/src/assets/img/warning/rain.svg +2 -7
  22. package/src/assets/img/warning/sea-icing.svg +2 -6
  23. package/src/assets/img/warning/sea-thunder-storm.svg +2 -5
  24. package/src/assets/img/warning/sea-water-height-high-water.svg +3 -8
  25. package/src/assets/img/warning/sea-water-height-shallow-water.svg +3 -7
  26. package/src/assets/img/warning/sea-wave-height.svg +4 -7
  27. package/src/assets/img/warning/sea-wind-legend.svg +2 -5
  28. package/src/assets/img/warning/sea-wind.svg +2 -5
  29. package/src/assets/img/warning/several.svg +2 -5
  30. package/src/assets/img/warning/thunder-storm.svg +2 -5
  31. package/src/assets/img/warning/traffic-weather.svg +2 -6
  32. package/src/assets/img/warning/uv-note.svg +2 -6
  33. package/src/assets/img/warning/wind.svg +2 -5
  34. package/src/components/AlertClient.vue +330 -251
  35. package/src/components/CollapsiblePanel.vue +281 -0
  36. package/src/components/DayLarge.vue +146 -110
  37. package/src/components/DaySmall.vue +97 -81
  38. package/src/components/Days.vue +229 -159
  39. package/src/components/DescriptionWarning.vue +63 -38
  40. package/src/components/GrayScaleToggle.vue +58 -54
  41. package/src/components/Legend.vue +102 -325
  42. package/src/components/MapLarge.vue +574 -351
  43. package/src/components/MapSmall.vue +137 -122
  44. package/src/components/PopupRow.vue +24 -12
  45. package/src/components/Region.vue +168 -118
  46. package/src/components/RegionWarning.vue +40 -33
  47. package/src/components/Regions.vue +189 -105
  48. package/src/components/Warning.vue +70 -45
  49. package/src/components/Warnings.vue +136 -72
  50. package/src/composables/useAlertClient.ts +360 -0
  51. package/src/composables/useConfig.ts +573 -0
  52. package/src/composables/useFields.ts +66 -0
  53. package/src/composables/useI18n.ts +62 -0
  54. package/src/composables/useKeyCodes.ts +16 -0
  55. package/src/composables/useMapPaths.ts +477 -0
  56. package/src/composables/useUtils.ts +683 -0
  57. package/src/composables/useWarningsProcessor.ts +1007 -0
  58. package/src/data/geometries.json +993 -0
  59. package/src/{main.js → main.ts} +1 -0
  60. package/src/mixins/geojsonsvg.d.ts +57 -0
  61. package/src/mixins/geojsonsvg.js +5 -3
  62. package/src/plugins/index.ts +5 -0
  63. package/src/scss/_utilities.scss +193 -0
  64. package/src/scss/constants.scss +2 -1
  65. package/src/scss/warningImages.scss +8 -3
  66. package/src/types/index.ts +509 -0
  67. package/src/vite-env.d.ts +23 -0
  68. package/src/vue.ts +41 -0
  69. package/svgo.config.js +45 -0
  70. package/tests/README.md +430 -0
  71. package/tests/fixtures/mockWarningData.ts +152 -0
  72. package/tests/integration/warning-flow.spec.ts +445 -0
  73. package/tests/setup.ts +41 -0
  74. package/tests/unit/components/AlertClient.spec.ts +701 -0
  75. package/tests/unit/components/DayLarge.spec.ts +348 -0
  76. package/tests/unit/components/DaySmall.spec.ts +352 -0
  77. package/tests/unit/components/Days.spec.ts +548 -0
  78. package/tests/unit/components/DescriptionWarning.spec.ts +385 -0
  79. package/tests/unit/components/GrayScaleToggle.spec.ts +318 -0
  80. package/tests/unit/components/Legend.spec.ts +295 -0
  81. package/tests/unit/components/MapLarge.spec.ts +448 -0
  82. package/tests/unit/components/MapSmall.spec.ts +367 -0
  83. package/tests/unit/components/PopupRow.spec.ts +270 -0
  84. package/tests/unit/components/Region.spec.ts +373 -0
  85. package/tests/unit/components/RegionWarning.snapshot.spec.ts +361 -0
  86. package/tests/unit/components/RegionWarning.spec.ts +381 -0
  87. package/tests/unit/components/Regions.spec.ts +503 -0
  88. package/tests/unit/components/Warning.snapshot.spec.ts +483 -0
  89. package/tests/unit/components/Warning.spec.ts +489 -0
  90. package/tests/unit/components/Warnings.spec.ts +343 -0
  91. package/tests/unit/components/__snapshots__/RegionWarning.snapshot.spec.ts.snap +41 -0
  92. package/tests/unit/components/__snapshots__/Warning.snapshot.spec.ts.snap +433 -0
  93. package/tests/unit/composables/useConfig.spec.ts +279 -0
  94. package/tests/unit/composables/useI18n.spec.ts +116 -0
  95. package/tests/unit/composables/useKeyCodes.spec.ts +27 -0
  96. package/tests/unit/composables/useUtils.spec.ts +213 -0
  97. package/tsconfig.json +43 -0
  98. package/tsconfig.node.json +11 -0
  99. package/vite.config.js +96 -26
  100. package/vitest.config.js +40 -0
  101. package/dist/favicon.ico +0 -0
  102. package/dist/index.dark.html +0 -20
  103. package/dist/index.en.html +0 -15
  104. package/dist/index.fi.html +0 -15
  105. package/dist/index.html +0 -15
  106. package/dist/index.js +0 -281
  107. package/dist/index.mjs +0 -281
  108. package/dist/index.mjs.map +0 -1
  109. package/dist/index.relative.html +0 -19
  110. package/dist/index.start.html +0 -20
  111. package/dist/index.sv.html +0 -15
  112. package/playwright.config.ts +0 -18
  113. package/public/index.relative.html +0 -19
  114. package/public/index.start.html +0 -20
  115. package/src/mixins/config.js +0 -1378
  116. package/src/mixins/fields.js +0 -26
  117. package/src/mixins/i18n.js +0 -25
  118. package/src/mixins/keycodes.js +0 -10
  119. package/src/mixins/panzoom.js +0 -900
  120. package/src/mixins/utils.js +0 -900
  121. package/src/plugins/index.js +0 -3
  122. package/test/snapshot.test.ts +0 -126
  123. package/vitest.config.ts +0 -6
@@ -49,7 +49,7 @@
49
49
  :theme="theme"
50
50
  :language="language"
51
51
  :spinner-enabled="spinnerEnabled"
52
- @daySelected="onDaySelected"
52
+ @day-selected="onDaySelected"
53
53
  @loaded="onLoaded" />
54
54
  </div>
55
55
  <div class="col-12 col-md-4 col-lg-4 col-xl-4 symbol-list">
@@ -60,8 +60,8 @@
60
60
  :gray-scale-selector="grayScaleSelector"
61
61
  :theme="theme"
62
62
  :language="language"
63
- @themeChanged="onThemeChanged"
64
- @warningsToggled="onWarningsToggled" />
63
+ @theme-changed="onThemeChanged"
64
+ @warnings-toggled="onWarningsToggled" />
65
65
  </div>
66
66
  </div>
67
67
  <div v-if="regionListEnabled" class="row">
@@ -82,262 +82,340 @@
82
82
  </div>
83
83
  </template>
84
84
 
85
- <script>
86
- import config from '../mixins/config'
87
- import i18n from '../mixins/i18n'
88
- import utils from '../mixins/utils'
85
+ <script setup lang="ts">
86
+ import {
87
+ ref,
88
+ computed,
89
+ watch,
90
+ onMounted,
91
+ onBeforeUnmount,
92
+ onServerPrefetch,
93
+ getCurrentInstance,
94
+ toRef,
95
+ } from 'vue'
89
96
  import Days from './Days.vue'
90
97
  import Legend from './Legend.vue'
91
98
  import Regions from './Regions.vue'
99
+ import { useConfig } from '@/composables/useConfig'
100
+ import { useI18n } from '@/composables/useI18n'
101
+ import { useWarningsProcessor } from '@/composables/useWarningsProcessor'
102
+ import {
103
+ regionsDefault,
104
+ isClientSide,
105
+ REGION_LAND,
106
+ REGION_SEA,
107
+ } from '@/composables/useUtils'
108
+ import geojsonsvg from '@/mixins/geojsonsvg'
109
+ import type {
110
+ WarningsMap,
111
+ Day,
112
+ LegendItem,
113
+ RegionsData,
114
+ WarningsDataResponse,
115
+ Language,
116
+ } from '@/types'
117
+ import type { ParentsMap } from '@/composables/useWarningsProcessor'
92
118
 
93
- export default {
94
- name: 'AlertClient',
95
- props: {
96
- refreshInterval: {
97
- type: Number,
98
- default: 1000 * 60 * 15,
99
- },
100
- defaultDay: {
101
- type: Number,
102
- default: 0,
103
- },
104
- staticDays: {
105
- type: Boolean,
106
- default: true,
107
- },
108
- startFrom: {
109
- type: String,
110
- default: '',
111
- },
112
- regionListEnabled: {
113
- type: Boolean,
114
- default: true,
115
- },
116
- grayScaleSelector: {
117
- type: Boolean,
118
- default: false,
119
- },
120
- currentTime: {
121
- type: Number,
122
- default: Date.now(),
123
- },
124
- warningsData: Object,
125
- dailyWarningTypes: {
126
- type: Array,
127
- default: () => [],
128
- },
129
- geometryId: {
130
- type: Number,
131
- default: config.props.defaultGeometryId,
132
- },
133
- language: {
134
- type: String,
135
- default: 'en',
136
- },
137
- theme: {
138
- type: String,
139
- default: 'light-theme',
140
- },
141
- loading: {
142
- type: Number,
143
- default: true,
144
- },
145
- sleep: {
146
- type: Boolean,
147
- default: true,
148
- },
149
- spinnerEnabled: {
150
- type: Boolean,
151
- default: true,
152
- },
153
- },
154
- components: {
155
- Days,
156
- Regions,
157
- Legend,
158
- },
159
- mixins: [config, i18n, utils],
160
- data() {
161
- return {
162
- selectedDay: this.defaultDay,
163
- visibleWarnings: [],
164
- timer: null,
165
- visibilityListener: null,
166
- warnings: null,
167
- days: [],
168
- regions: this.regionsDefault(),
169
- parents: {},
170
- legend: [],
171
- timeOffset: 0,
172
- // eslint-disable-next-line no-undef
173
- version: __APP_VERSION__,
174
- errors: [],
175
- }
176
- },
177
- computed: {
178
- toContentText() {
179
- if (
180
- [this.REGION_LAND, this.REGION_SEA].some(
181
- (regionType) =>
182
- this?.regions?.[this.selectedDay]?.[regionType]?.length > 0
183
- )
184
- ) {
185
- return this.t('toContent') || ''
186
- }
187
- return this.t('toNextContent') || ''
188
- },
189
- toContentId() {
190
- if (
191
- [this.REGION_LAND, this.REGION_SEA].some(
192
- (regionType) =>
193
- this?.regions?.[this.selectedDay]?.[regionType]?.length > 0
194
- )
195
- ) {
196
- return '#fmi-warnings-region-content'
197
- }
198
- return '#fmi-warnings-end-of-regions'
199
- },
200
- noWarningsText() {
201
- return this.t('noWarnings')
202
- },
203
- validWarningsText() {
204
- return this.legend.length > 0
205
- ? this.t('validWarnings')
206
- : this.t('noWarnings')
207
- },
208
- supportedBrowsersLink() {
209
- return this.t('supportedBrowsersLink')
210
- },
211
- supportedBrowsers() {
212
- return this.t('supportedBrowsers')
213
- },
214
- mainInfoText() {
215
- return this.loading === -1
216
- ? this.t('failed')
217
- : this.t('notInitializedStart')
218
- },
219
- additionalInfoText() {
220
- return this.loading === -1 ? '' : this.t('notInitializedEnd')
221
- },
222
- numWarnings() {
223
- return this.warnings != null ? Object.keys(this.warnings).length : 0
224
- },
225
- validData() {
226
- return (
227
- this.days != null &&
228
- this.days.length === 5 &&
229
- this.days[0].updatedDate != null &&
230
- this.days[0].updatedDate.length > 0
231
- )
232
- },
233
- },
234
- watch: {
235
- warningsData() {
236
- this.createDataForChildren()
237
- },
238
- },
239
- created() {
240
- this.createDataForChildren()
241
- if (this.warningsData == null) {
242
- this.update()
119
+ // Props
120
+ const props = withDefaults(
121
+ defineProps<{
122
+ refreshInterval?: number
123
+ defaultDay?: number
124
+ staticDays?: boolean
125
+ startFrom?: string
126
+ regionListEnabled?: boolean
127
+ grayScaleSelector?: boolean
128
+ currentTime?: number
129
+ warningsData?: WarningsDataResponse | null
130
+ dailyWarningTypes?: string[]
131
+ geometryId?: number
132
+ language?: Language
133
+ theme?: string
134
+ loading?: number
135
+ sleep?: boolean
136
+ spinnerEnabled?: boolean
137
+ }>(),
138
+ {
139
+ refreshInterval: 1000 * 60 * 15,
140
+ defaultDay: 0,
141
+ staticDays: true,
142
+ startFrom: '',
143
+ regionListEnabled: true,
144
+ grayScaleSelector: false,
145
+ currentTime: () => Date.now(),
146
+ warningsData: null,
147
+ dailyWarningTypes: () => [],
148
+ geometryId: 2021,
149
+ language: 'en',
150
+ theme: 'light-theme',
151
+ loading: 1,
152
+ sleep: true,
153
+ spinnerEnabled: true,
154
+ }
155
+ )
156
+
157
+ // Emits
158
+ const emit = defineEmits<{
159
+ loaded: [value: number]
160
+ themeChanged: [theme: string]
161
+ 'update-warnings': []
162
+ }>()
163
+
164
+ // Config
165
+ const config = useConfig()
166
+
167
+ // i18n
168
+ const { t } = useI18n(toRef(props, 'language'))
169
+
170
+ // Types
171
+ type DayIndex = 0 | 1 | 2 | 3 | 4
172
+
173
+ // State
174
+ const selectedDay = ref<DayIndex>(props.defaultDay as DayIndex)
175
+ const visibleWarnings = ref<string[]>([])
176
+ const timer = ref<ReturnType<typeof setInterval> | null>(null)
177
+ const visibilityListener = ref<(() => void) | null>(null)
178
+ const warnings = ref<WarningsMap | null>(null)
179
+ const days = ref<Day[]>([])
180
+ const regions = ref<RegionsData>(regionsDefault())
181
+ const parents = ref<ParentsMap>({})
182
+ const legend = ref<LegendItem[]>([])
183
+ const timeOffset = ref(0)
184
+ // eslint-disable-next-line no-undef
185
+ const version = __APP_VERSION__
186
+ const errors = ref<string[]>([])
187
+
188
+ // Create bound geoJSONToSVG function
189
+ const geoJSONToSVG = geojsonsvg.methods.geoJSONToSVG.bind(geojsonsvg.methods)
190
+
191
+ // Create refs for useWarningsProcessor options
192
+ const geometryIdRef = computed(() => String(props.geometryId))
193
+ const geometriesRef = computed(() => config.geometries)
194
+ const regionIdsRef = computed(() => config.regionIds)
195
+ const warningTypesRef = computed(() => config.warningTypes)
196
+ const timeZoneRef = computed(() => config.timeZone)
197
+ const localeRef = computed(() => config.dateTimeFormatLocale)
198
+ const currentTimeRef = computed(() => props.currentTime)
199
+ const startFromRef = computed(() => props.startFrom)
200
+ const staticDaysRef = computed(() => props.staticDays)
201
+ const dailyWarningTypesRef = computed(() => props.dailyWarningTypes)
202
+ const maxUpdateDelayRef = computed(
203
+ () =>
204
+ config.maxUpdateDelay as {
205
+ weather_update_time: number
206
+ flood_update_time: number
243
207
  }
244
- },
245
- mounted() {
246
- this.initTimer()
247
- if (this.isClientSide() && this.sleep) {
248
- this.visibilityListener = document.addEventListener(
249
- 'visibilitychange',
250
- this.visibilityChange
251
- )
208
+ )
209
+ const bboxRef = computed(
210
+ () => config.bbox as unknown as import('@/types').GeoJSONFeature
211
+ )
212
+
213
+ // Error handlers
214
+ const handleError = (error: string) => {
215
+ if (!errors.value.includes(error)) {
216
+ errors.value.push(error)
217
+ }
218
+ console.log(error)
219
+ }
220
+
221
+ const onDataError = () => {
222
+ emit('loaded', -1)
223
+ }
224
+
225
+ // Warnings processor
226
+ const { handleMapWarnings } = useWarningsProcessor({
227
+ geometryId: geometryIdRef,
228
+ geometries: geometriesRef,
229
+ regionIds: regionIdsRef,
230
+ warningTypes: warningTypesRef,
231
+ timeZone: timeZoneRef,
232
+ locale: localeRef,
233
+ currentTime: currentTimeRef,
234
+ startFrom: startFromRef,
235
+ staticDays: staticDaysRef,
236
+ dailyWarningTypes: dailyWarningTypesRef,
237
+ maxUpdateDelay: maxUpdateDelayRef,
238
+ bbox: bboxRef,
239
+ geoJSONToSVG,
240
+ t,
241
+ handleError,
242
+ onDataError,
243
+ })
244
+
245
+ // Computed
246
+ const toContentText = computed(() => {
247
+ if (
248
+ [REGION_LAND, REGION_SEA].some(
249
+ (regionType) =>
250
+ ((
251
+ regions.value?.[selectedDay.value] as
252
+ | Record<string, unknown[]>
253
+ | undefined
254
+ )?.[regionType]?.length ?? 0) > 0
255
+ )
256
+ ) {
257
+ return t('toContent') || ''
258
+ }
259
+ return t('toNextContent') || ''
260
+ })
261
+
262
+ const toContentId = computed(() => {
263
+ if (
264
+ [REGION_LAND, REGION_SEA].some(
265
+ (regionType) =>
266
+ ((
267
+ regions.value?.[selectedDay.value] as
268
+ | Record<string, unknown[]>
269
+ | undefined
270
+ )?.[regionType]?.length ?? 0) > 0
271
+ )
272
+ ) {
273
+ return '#fmi-warnings-region-content'
274
+ }
275
+ return '#fmi-warnings-end-of-regions'
276
+ })
277
+
278
+ const noWarningsText = computed(() => t('noWarnings'))
279
+
280
+ const validWarningsText = computed(() =>
281
+ legend.value.length > 0 ? t('validWarnings') : t('noWarnings')
282
+ )
283
+
284
+ const supportedBrowsersLink = computed(() => t('supportedBrowsersLink'))
285
+
286
+ const supportedBrowsers = computed(() => t('supportedBrowsers'))
287
+
288
+ const mainInfoText = computed(() =>
289
+ props.loading === -1 ? t('failed') : t('notInitializedStart')
290
+ )
291
+
292
+ const additionalInfoText = computed(() =>
293
+ props.loading === -1 ? '' : t('notInitializedEnd')
294
+ )
295
+
296
+ const numWarnings = computed(() =>
297
+ warnings.value != null ? Object.keys(warnings.value).length : 0
298
+ )
299
+
300
+ const validData = computed(
301
+ () =>
302
+ days.value != null &&
303
+ days.value.length === 5 &&
304
+ days.value[0]?.updatedDate != null &&
305
+ days.value[0]?.updatedDate.length > 0
306
+ )
307
+
308
+ // Methods
309
+ const onDaySelected = (newSelectedDay: number) => {
310
+ selectedDay.value = newSelectedDay as DayIndex
311
+ }
312
+
313
+ const onWarningsToggled = (newVisibleWarnings: string[]) => {
314
+ visibleWarnings.value = newVisibleWarnings
315
+ legend.value.forEach((warning, i) => {
316
+ const isVisible = newVisibleWarnings.includes(warning.type)
317
+ if (isVisible !== warning.visible && legend.value[i]) {
318
+ legend.value[i].visible = isVisible
252
319
  }
253
- },
254
- beforeDestroy() {
255
- if (this.isClientSide()) {
256
- document.removeEventListener('visibilitychange', this.visibilityListener)
320
+ })
321
+ }
322
+
323
+ const onLoaded = (loaded: boolean) => {
324
+ if (props.loading !== -1 && loaded) {
325
+ emit('loaded', 1)
326
+ }
327
+ }
328
+
329
+ const onThemeChanged = (newTheme: string) => {
330
+ if (props.theme !== newTheme) {
331
+ emit('themeChanged', newTheme)
332
+ }
333
+ }
334
+
335
+ const toContentClicked = () => {
336
+ const instance = getCurrentInstance()
337
+ const el = instance?.proxy?.$el as HTMLElement | undefined
338
+ const textContent = el?.querySelector(toContentId.value) as HTMLElement | null
339
+ textContent?.scrollIntoView()
340
+ textContent?.focus()
341
+ }
342
+
343
+ const createDataForChildren = () => {
344
+ if (props.warningsData != null) {
345
+ const result = handleMapWarnings(props.warningsData)
346
+ warnings.value = result.warnings
347
+ days.value = result.days
348
+ regions.value = result.regions
349
+ parents.value = result.parents
350
+ legend.value = result.legend
351
+ visibleWarnings.value = legend.value
352
+ .filter((legendWarning) => legendWarning.visible)
353
+ .map((legendWarning) => legendWarning.type)
354
+ }
355
+ }
356
+
357
+ const visibilityChange = () => {
358
+ if (isClientSide() && props.refreshInterval) {
359
+ if (document.hidden) {
360
+ cancelTimer()
361
+ } else {
362
+ cancelTimer()
363
+ update()
364
+ initTimer()
257
365
  }
258
- this.cancelTimer()
259
- },
260
- serverPrefetch() {
261
- this.createDataForChildren()
262
- },
263
- methods: {
264
- onDaySelected(newSelectedDay) {
265
- this.selectedDay = newSelectedDay
266
- },
267
- onWarningsToggled(newVisibleWarnings) {
268
- this.visibleWarnings = newVisibleWarnings
269
- this.legend.forEach((warning, i) => {
270
- const isVisible = newVisibleWarnings.includes(warning.type)
271
- if (isVisible !== warning.visible) {
272
- this.legend[i].visible = isVisible
273
- }
274
- })
275
- },
276
- onLoaded(loaded) {
277
- if (this.loading !== -1 && loaded) {
278
- this.$emit('loaded', 1)
279
- }
280
- },
281
- onDataError() {
282
- this.$emit('loaded', -1)
283
- },
284
- onThemeChanged(newTheme) {
285
- if (this.theme !== newTheme) {
286
- this.$emit('themeChanged', newTheme)
287
- }
288
- },
289
- toContentClicked() {
290
- const textContent = this.$el.querySelector(this.toContentId)
291
- textContent.scrollIntoView()
292
- textContent.focus()
293
- },
294
- createDataForChildren() {
295
- if (this.warningsData != null) {
296
- const result = this.handleMapWarnings(this.warningsData)
297
- this.warnings = result.warnings
298
- this.days = result.days
299
- this.regions = result.regions
300
- this.parents = result.parents
301
- this.legend = result.legend
302
- this.visibleWarnings = this.legend
303
- .filter((legendWarning) => legendWarning.visible)
304
- .map((legendWarning) => legendWarning.type)
305
- }
306
- },
307
- visibilityChange() {
308
- if (this.isClientSide() && this.refreshInterval) {
309
- if (document.hidden) {
310
- this.cancelTimer()
311
- } else {
312
- this.cancelTimer()
313
- this.update()
314
- this.initTimer()
315
- }
316
- }
317
- },
318
- initTimer() {
319
- if (this.refreshInterval) {
320
- this.timer = setInterval(this.update, this.refreshInterval)
321
- }
322
- },
323
- cancelTimer() {
324
- if (this.timer != null) {
325
- clearInterval(this.timer)
326
- }
327
- },
328
- update() {
329
- if (this.refreshInterval > 0) {
330
- this.$emit('update-warnings')
331
- }
332
- },
333
- handleError(error) {
334
- if (!this.errors.includes(error)) {
335
- this.errors.push(error)
336
- }
337
- console.log(error)
338
- },
339
- },
366
+ }
340
367
  }
368
+
369
+ const initTimer = () => {
370
+ if (props.refreshInterval) {
371
+ timer.value = setInterval(update, props.refreshInterval)
372
+ }
373
+ }
374
+
375
+ const cancelTimer = () => {
376
+ if (timer.value != null) {
377
+ clearInterval(timer.value)
378
+ }
379
+ }
380
+
381
+ const update = () => {
382
+ if (props.refreshInterval > 0) {
383
+ emit('update-warnings')
384
+ }
385
+ }
386
+
387
+ // Watch
388
+ watch(
389
+ () => props.warningsData,
390
+ () => {
391
+ createDataForChildren()
392
+ }
393
+ )
394
+
395
+ // Lifecycle
396
+ createDataForChildren()
397
+ if (props.warningsData == null) {
398
+ update()
399
+ }
400
+
401
+ onMounted(() => {
402
+ initTimer()
403
+ if (isClientSide() && props.sleep) {
404
+ document.addEventListener('visibilitychange', visibilityChange)
405
+ visibilityListener.value = visibilityChange
406
+ }
407
+ })
408
+
409
+ onBeforeUnmount(() => {
410
+ if (isClientSide() && visibilityListener.value) {
411
+ document.removeEventListener('visibilitychange', visibilityListener.value)
412
+ }
413
+ cancelTimer()
414
+ })
415
+
416
+ onServerPrefetch(() => {
417
+ createDataForChildren()
418
+ })
341
419
  </script>
342
420
 
343
421
  <style scoped lang="scss">
@@ -401,6 +479,7 @@ div#fmi-warnings {
401
479
  h2.valid-warnings {
402
480
  text-align: left;
403
481
  font-weight: bold;
482
+ margin-top: 0;
404
483
  margin-bottom: 3px;
405
484
  }
406
485