From 8fdf98af10452250aa1f1cbade461e58b612582c Mon Sep 17 00:00:00 2001 From: "NVE\\omy" Date: Thu, 11 Jun 2026 14:06:59 +0200 Subject: [PATCH 1/6] =?UTF-8?q?feat(nve-exposed-height):=20Ny=20komponent?= =?UTF-8?q?=20for=20utsatt=20h=C3=B8yde?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc-site/components/nve-exposed-height.md | 107 ++++++++ .../nve-exposed-height/AvalancheRose.js | 249 ++++++++++++++++++ .../nve-exposed-height.component.ts | 221 ++++++++++++++++ .../nve-exposed-height.styles.ts | 37 +++ .../nve-exposed-height.test.ts | 20 ++ src/nve-designsystem.ts | 2 + 6 files changed, 636 insertions(+) create mode 100644 doc-site/components/nve-exposed-height.md create mode 100644 src/components/nve-exposed-height/AvalancheRose.js create mode 100644 src/components/nve-exposed-height/nve-exposed-height.component.ts create mode 100644 src/components/nve-exposed-height/nve-exposed-height.styles.ts create mode 100644 src/components/nve-exposed-height/nve-exposed-height.test.ts diff --git a/doc-site/components/nve-exposed-height.md b/doc-site/components/nve-exposed-height.md new file mode 100644 index 00000000..d6de385c --- /dev/null +++ b/doc-site/components/nve-exposed-height.md @@ -0,0 +1,107 @@ +--- +layout: component +outline: [2, 3] +--- + +# Utsatt høyde + +Viser utsatt høyde for et skredproblem. Komponenten bruker et fjellsymbol med piler og høydetekst for å indikere hvilke høyder som er utsatt. + + + +```html + +``` + + + +Komponenten er skalerbar og standard høyde er 90px. Du kan endre størrelsen med CSS-variabelen `--exposed-height-size`. + +## Eksempler + +### Varianter + +Bruk `variant` for å angi om skredproblemet gjelder: + +1. over en viss høyde +2. under en viss høyde +3. både over og under visse høyder +4. mellom to høyder + +Variant 1 er standard. +For variant 1 og 2 må du sende inn `height1`. +For variant 3 og 4 må du sende inn både `height1` og `height2`. + + + +```html + + + + +``` + + + +#### Feil bruk av variant og høyde + +Her er noen eksempler på feil bruk: + + + +```html + utsatt høyde blir over 0m om variant og height1 ikke angis + variant 2 mangler height1 + variant 3 må ha både height 1 og height2 + variant 4 må ha både height 1 og height2 +``` + + + +### Størrelse + +Endre høyden med CSS-variabelen `--exposed-height-size`. Standard høyde er 90px. + + + +```html + 120px høyde +90px høyde +``` + + + +### Språk + +Bruk `lang="en"` for å vise aria-label på engelsk. Norsk er standard. + + + +```html +Engelsk: + + + + + +Norsk: + + + + +``` + + + +### Farger + +Bruk css-variablene `--exposed-height-affected-color` og `--exposed-height-unaffected-color` for å overstyre fargene. + + + +```html + + +``` + + diff --git a/src/components/nve-exposed-height/AvalancheRose.js b/src/components/nve-exposed-height/AvalancheRose.js new file mode 100644 index 00000000..b71dae56 --- /dev/null +++ b/src/components/nve-exposed-height/AvalancheRose.js @@ -0,0 +1,249 @@ +(function () { + + function drawAvalacheRose(element) { + + var validExpositions = $(element).data("valid-expositions").toString(); + var exposedHeight1 = $(element).data("exposed-height1"); + var exposedHeight2 = $(element).data("exposed-height2"); + var exposedHeightFill = $(element).data("exposed-height-fill"); + + var container = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + container.setAttribute('transform', 'scale(' + 1 + ')'); + + var radius = 36; + var cx = radius + 0.25 * radius; + var cy = cx; + + container.appendChild(createCake(radius, cx, cy, validExpositions.split(''))); + + container.appendChild(createLabel(cx, cy - radius, 0.2 * radius, 'N')); + container.appendChild(createLabel(cx + radius, cy, 0.2 * radius, 'Ø')); + container.appendChild(createLabel(cx, cy + radius, 0.2 * radius, 'S')); + container.appendChild(createLabel(cx - radius, cy, 0.2 * radius, 'V')); + + if (exposedHeightFill === 1) { + container.appendChild(createMountain(cx + 1.3 * radius, cy, radius, exposedHeightFill)); + container.appendChild(createUpArrow(cx + 2.8 * radius, cy + radius - 30, 11.92, 16)); + var label = createHeightLabel(cx + 2.8 * radius + 11.92 / 2, cy + radius, radius, exposedHeight1 + 'm'); + label.classList.add('centered'); + container.appendChild(label); + } else if (exposedHeightFill === 2) { + container.appendChild(createMountain(cx + 1.3 * radius, cy, radius, exposedHeightFill)); + container.appendChild(createDownArrow(cx + 2.8 * radius, cy + radius - 16, 11.92, 16)); + var label = createHeightLabel(cx + 2.8 * radius + 11.92 / 2, cy + 16, radius, exposedHeight1 + 'm'); + label.classList.add('centered'); + container.appendChild(label); + } else if (exposedHeightFill === 3) { + container.appendChild(createMountain(cx + 1.3 * radius, cy - 6, radius, exposedHeightFill)); + container.appendChild(createUpArrow(cx + 2.7 * radius, cy + radius - 40, 11.92, 16)); + container.appendChild(createDownArrow(cx + 2.7 * radius, cy + radius - 16, 11.92, 16)); + container.appendChild(createHeightLabel(cx + 2.8 * radius + 16, cy + 10, radius, exposedHeight1 + 'm')); + container.appendChild(createHeightLabel(cx + 2.8 * radius + 16, cy + radius - 8, radius, exposedHeight2 + 'm')); + } else if (exposedHeightFill === 4) { + container.appendChild(createMountain(cx + 1.3 * radius, cy - 6, radius, exposedHeightFill)); + container.appendChild(createDownArrow(cx + 2.7 * radius, cy + radius - 48, 11.92, 16)); + container.appendChild(createUpArrow(cx + 2.7 * radius, cy + radius - 16, 11.92, 16)); + container.appendChild(createHeightLabel(cx + 2.7 * radius, cy + 16, radius, exposedHeight2 + '-' + exposedHeight1 + 'm')); + } + + var svgElement = element.children().get(0); + + svgElement.appendChild(container); + } + + function createCake(radius, cx, cy, activeSectors) { + var n = activeSectors.length; + var sectorAngle = 360 / n; + var x1 = cx; + var y1 = cy - radius; + var x2 = cx + Math.cos(sectorAngle * Math.PI / 180) * radius; + var y2 = cy - Math.sin(sectorAngle * Math.PI / 180) * radius; + var container = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + + for (var i = 0; i < n; i++) { + var sector = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + sector.setAttribute('d', + 'M ' + x1 + ' ' + y1 + ' ' + + 'A ' + radius + ' ' + radius + ' 0 0 1 ' + x2 + ' ' + y2 + ' ' + + 'L ' + cx + ' ' + cy + ' ' + + 'Z'); + sector.setAttribute('transform', 'rotate(' + (-22.5 + i * sectorAngle) + ' ' + cx + ' ' + cy + ')'); + sector.classList.add('sector'); + if (activeSectors[i] == 1) { + sector.classList.add('active'); + } + container.appendChild(sector); + } + + var outline = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); + outline.setAttribute('cx', cx); + outline.setAttribute('cy', cy); + outline.setAttribute('r', radius); + outline.setAttribute('fill', 'none'); + outline.setAttribute('stroke-width', '1'); + outline.classList.add('circle-outline'); + container.appendChild(outline); + + return container; + } + + function createLabel(x, y, r, text) { + var fontSize = 1.1 * r; + var container = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + + var svgCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); + svgCircle.setAttribute('cx', x); + svgCircle.setAttribute('cy', y); + svgCircle.setAttribute('r', r); + svgCircle.classList.add('circle-label'); + container.appendChild(svgCircle); + + var svgText = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + svgText.setAttribute('x', x); + svgText.setAttribute('y', y); + svgText.setAttribute('dy', (0.4 * fontSize) + ''); + svgText.setAttribute('font-size', fontSize + ''); + svgText.classList.add('centered'); + svgText.textContent = text; + container.appendChild(svgText); + + return container; + } + + function createMountain(x, y, width, exposedHeightFill) { + var container = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + + if (exposedHeightFill == 1) { + + var top = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + top.setAttribute('d', 'M34.1299,22.8691 L20.1809,1.0741 C19.7079,0.3711 19.1749,0.0461 18.5749,0.0961 C17.9749,0.1461 17.4999,0.5581 17.1459,1.3301 L8.5139,18.9511 C10.8769,18.8161 12.7759,20.6831 12.7759,20.6831 C13.8629,21.6761 14.2179,22.0151 16.1509,22.0291 C19.1399,22.0511 20.0179,20.7691 22.0269,21.4881 C24.3919,22.3371 25.3159,23.3921 27.1709,24.5401 C29.9429,26.2481 31.3449,26.2291 33.0259,25.1611 C33.0259,25.1611 34.6099,24.2071 34.1299,22.8691'); + top.setAttribute('fill', '#d21523'); + top.setAttribute('stroke-width', '1'); + top.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); + container.appendChild(top); + + var bottom = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + bottom.setAttribute('d', 'M6.4121,23.752 L1.0791,34.822 C0.6321,35.565 0.5661,36.549 0.8771,37.084 C1.1851,37.623 1.7671,38.58 2.6191,38.58 L39.6821,38.58 C40.5341,38.58 41.1151,37.623 41.4251,37.084 C41.7341,36.549 41.6681,35.219 41.2231,34.479 L36.2291,26.217 C36.4201,27.445 34.3141,28.854 34.3141,28.854 C32.6091,29.992 30.3481,29.959 27.8371,28.975 C25.7441,28.152 24.4491,26.895 22.4571,25.955 C20.4671,25.012 19.3401,26.041 16.5901,26.281 C14.9531,26.424 13.9711,26.166 12.6221,25.324 C12.6221,25.324 9.9561,23.752 6.4601,23.752 L6.4121,23.752 Z'); + bottom.setAttribute('fill', '#E3E3E3'); + bottom.setAttribute('stroke-width', '1'); + bottom.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); + container.appendChild(bottom); + + } else if (exposedHeightFill == 2) { + + var top = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + top.setAttribute('d', 'M34.1299,23.0034 L20.1809,1.2094 C19.7079,0.5054 19.1749,0.1804 18.5749,0.2304 C17.9749,0.2814 17.4999,0.6934 17.1459,1.4644 L8.5139,19.0864 C10.8769,18.9514 12.7759,20.8184 12.7759,20.8184 C13.8629,21.8104 14.2179,22.1504 16.1509,22.1644 C19.1399,22.1854 20.0179,20.9044 22.0269,21.6234 C24.3919,22.4714 25.3159,23.5274 27.1709,24.6744 C29.9429,26.3824 31.3449,26.3644 33.0259,25.2954 C33.0259,25.2954 34.6099,24.3414 34.1299,23.0034 L34.1299,23.0034 Z'); + top.setAttribute('fill', '#E3E3E3'); + top.setAttribute('stroke-width', '1'); + top.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); + container.appendChild(top); + + var bottom = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + bottom.setAttribute('d', 'M6.4121,23.8867 L1.0791,34.9567 C0.6321,35.6997 0.5661,36.6837 0.8771,37.2187 C1.1851,37.7577 1.7671,38.7147 2.6191,38.7147 L39.6821,38.7147 C40.5341,38.7147 41.1151,37.7577 41.4251,37.2187 C41.7341,36.6837 41.6681,35.3537 41.2231,34.6137 L36.2291,26.3517 C36.4201,27.5797 34.3141,28.9887 34.3141,28.9887 C32.6091,30.1267 30.3481,30.0937 27.8371,29.1097 C25.7441,28.2867 24.4491,27.0297 22.4571,26.0897 C20.4671,25.1467 19.3401,26.1757 16.5901,26.4157 C14.9531,26.5587 13.9711,26.3007 12.6221,25.4587 C12.6221,25.4587 9.9561,23.8867 6.4601,23.8867 L6.4121,23.8867 Z'); + bottom.setAttribute('fill', '#d21523'); + bottom.setAttribute('stroke-width', '1'); + bottom.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); + container.appendChild(bottom); + + } else if (exposedHeightFill == 3) { + + var top = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + top.setAttribute('d', 'M23.917,10.0605 L19.813,2.6985 C19.387,2.0665 18.906,1.7735 18.367,1.8185 C17.826,1.8645 17.4,2.2345 17.08,2.9285 L14.471,8.8895 L14.384,9.0545 C14.384,9.0545 16.84,8.2885 18.863,9.4345 C20.521,10.3725 21.144,11.3805 23.917,10.0605'); + top.setAttribute('fill', '#d21523'); + top.setAttribute('stroke-width', '1'); + top.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); + container.appendChild(top); + + var middle = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + middle.setAttribute('d', 'M34.2402,27.625 C34.2402,28.483 33.0472,29.218 33.0472,29.218 C31.3652,30.286 29.3092,30.762 26.6252,28.92 C24.6822,27.587 23.7362,26.562 21.4802,25.868 C19.4402,25.241 18.5942,26.431 15.6052,26.409 C13.6722,26.396 13.2832,25.827 12.2282,25.063 C12.2282,25.063 10.6742,23.747 8.6982,23.268 L8.0602,23.169 L12.9712,12.208 C12.9712,12.208 16.3302,11.472 18.5802,12.745 C20.4222,13.788 23.3102,15.342 25.7672,13.209 L34.2402,27.625'); + middle.setAttribute('fill', '#E3E3E3'); + middle.setAttribute('stroke-width', '1'); + middle.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); + container.appendChild(middle); + + var bottom = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + bottom.setAttribute('d', 'M5.9922,27.7842 L0.8322,39.2022 C0.3852,39.9442 0.3202,40.9292 0.6312,41.4642 C0.9392,42.0032 1.5212,42.9602 2.3732,42.9602 L39.4352,42.9602 C40.2872,42.9602 40.8692,42.0032 41.1782,41.4642 C41.4882,40.9292 41.4222,39.5982 40.9762,38.8582 L36.0102,30.6382 C35.9552,31.7022 34.0682,33.2332 34.0682,33.2332 C32.3632,34.3722 30.1012,34.3392 27.5902,33.3542 C25.4982,32.5322 24.2032,31.2742 22.2112,30.3352 C20.2212,29.3912 19.0942,30.4212 16.3442,30.6612 C14.7072,30.8042 13.7242,30.5462 12.3752,29.7042 C12.3752,29.7042 9.9982,27.7842 6.5022,27.7842 L5.9922,27.7842 Z'); + bottom.setAttribute('fill', '#d21523'); + bottom.setAttribute('stroke-width', '1'); + bottom.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); + container.appendChild(bottom); + + } else if (exposedHeightFill == 4) { + + var top = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + top.setAttribute('d', 'M23.917,14.0537 L19.813,6.6917 C19.387,6.0597 18.906,5.7667 18.367,5.8117 C17.826,5.8577 17.4,6.2277 17.08,6.9217 L14.471,12.8827 L14.384,13.0477 C14.384,13.0477 16.84,12.2817 18.863,13.4277 C20.521,14.3657 21.144,15.3737 23.917,14.0537'); + top.setAttribute('fill', '#E3E3E3'); + top.setAttribute('stroke-width', '1'); + top.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); + container.appendChild(top); + + var middle = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + middle.setAttribute('d', 'M34.2402,31.6182 C34.2402,32.4762 33.0472,33.2112 33.0472,33.2112 C31.3652,34.2792 29.3092,34.7552 26.6252,32.9132 C24.6822,31.5802 23.7362,30.5552 21.4802,29.8612 C19.4402,29.2342 18.5942,30.4242 15.6052,30.4022 C13.6722,30.3892 13.2832,29.8202 12.2282,29.0562 C12.2282,29.0562 10.6742,27.7402 8.6982,27.2612 L8.0602,27.1622 L12.9712,16.2012 C12.9712,16.2012 16.3302,15.4652 18.5802,16.7382 C20.4222,17.7812 23.3102,19.3352 25.7672,17.2022 L34.2402,31.6182'); + middle.setAttribute('fill', '#d21523'); + middle.setAttribute('stroke-width', '1'); + middle.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); + container.appendChild(middle); + + var bottom = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + bottom.setAttribute('d', 'M5.9922,31.7773 L0.8322,43.1953 C0.3852,43.9373 0.3202,44.9223 0.6312,45.4573 C0.9392,45.9963 1.5212,46.9533 2.3732,46.9533 L39.4352,46.9533 C40.2872,46.9533 40.8692,45.9963 41.1782,45.4573 C41.4882,44.9223 41.4222,43.5913 40.9762,42.8513 L36.0102,34.6313 C35.9552,35.6953 34.0682,37.2263 34.0682,37.2263 C32.3632,38.3653 30.1012,38.3323 27.5902,37.3473 C25.4982,36.5253 24.2032,35.2673 22.2112,34.3283 C20.2212,33.3843 19.0942,34.4143 16.3442,34.6543 C14.7072,34.7973 13.7242,34.5393 12.3752,33.6973 C12.3752,33.6973 9.9982,31.7773 6.5022,31.7773 L5.9922,31.7773 Z'); + bottom.setAttribute('fill', '#E3E3E3'); + bottom.setAttribute('stroke-width', '1'); + bottom.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); + container.appendChild(bottom); + + } + + return container; + } + + function createHeightLabel(x, y, r, text) { + var fontSize = 0.4 * r; + var svgText = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + svgText.setAttribute('x', x); + svgText.setAttribute('y', y); + //svgText.setAttribute("dy", (0.4 * fontSize) + ""); + svgText.setAttribute('font-size', fontSize + ''); + svgText.textContent = text; + return svgText; + } + + function createUpArrow(x, y, width, height) { + var container = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + var arrow = createArrow(width, height); + container.appendChild(arrow); + container.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); + return container; + } + + function createDownArrow(x, y, width, height) { + var container = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + var arrow = createArrow(width, height); + arrow.setAttribute('transform', 'rotate(180 ' + (width / 2) + ' ' + (height / 2) + ')'); + + container.appendChild(arrow); + container.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); + return container; + } + + function createArrow(width, height) { + var shaftWidth = 0.3334 * width; + var shaftHeight = 0.5 * height; + + var arrow = document.createElementNS('http://www.w3.org/2000/svg', 'polygon'); + arrow.setAttribute('points', + width + ' ' + (height - shaftHeight) + ' ' + + (width / 2 + shaftWidth / 2) + ' ' + (height - shaftHeight) + ' ' + + (width / 2 + shaftWidth / 2) + ' ' + (height) + ' ' + + (width / 2 - shaftWidth / 2) + ' ' + (height) + ' ' + + (width / 2 - shaftWidth / 2) + ' ' + (height - shaftHeight) + ' ' + + '0 ' + (height - shaftHeight) + ' ' + + (width / 2) + ' 0'); + arrow.classList.add('arrow'); + + return arrow; + } + + $("nve-avalanche-rose").each(function () { + drawAvalacheRose($(this)); + }); +})(); \ No newline at end of file diff --git a/src/components/nve-exposed-height/nve-exposed-height.component.ts b/src/components/nve-exposed-height/nve-exposed-height.component.ts new file mode 100644 index 00000000..ebdf8e9b --- /dev/null +++ b/src/components/nve-exposed-height/nve-exposed-height.component.ts @@ -0,0 +1,221 @@ +import { html, LitElement, svg } from 'lit'; +import { customElement, property } from 'lit/decorators.js'; +import { INveComponent } from '@interfaces/NveComponent.interface'; +import styles from './nve-exposed-height.styles'; + +interface MountainPaths { + top: string; + bottom: string; + middle?: string; + topClass: string; + bottomClass: string; + middleClass?: string; +} + +/** + * Visualiserer utsatt høyde for skredproblemer. + * Viser et fjellsymbol med piler og høydeindikasjon. + * @cssproperty --exposed-height-affected-color - Farge på utsatte høyde. Standard er #d21523. + * @cssproperty --exposed-height-unaffected-color - Farge på ikke-utsatt høyde. Standard er #e3e3e3. + */ +@customElement('nve-exposed-height') +export default class NveExposedHeight extends LitElement implements INveComponent { + /** Type fyll: 1=over, 2=under, 3=over og under, 4=mellom */ + @property({ type: Number }) variant: 1 | 2 | 3 | 4 = 1; + + /** Første høydeverdi i meter */ + @property({ type: Number }) height1: number = 0; + + /** Andre høydeverdi i meter (for variant 3 og 4) */ + @property({ type: Number }) height2: number = 0; + + /** Språk for aria-label: 'no' for norsk, 'en' for engelsk. Norsk er standard. */ + @property({ type: String }) lang: 'no' | 'en' = 'no'; + + @property({ type: String }) testId: string | undefined = undefined; + + static styles = [styles]; + + private createMountainPath(variant: 1 | 2 | 3 | 4): MountainPaths { + // Variant 1 og 2 bruker samme geometri + const twoPartMountain = { + top: 'M34.1299,22.8691 L20.1809,1.0741 C19.7079,0.3711 19.1749,0.0461 18.5749,0.0961 C17.9749,0.1461 17.4999,0.5581 17.1459,1.3301 L8.5139,18.9511 C10.8769,18.8161 12.7759,20.6831 12.7759,20.6831 C13.8629,21.6761 14.2179,22.0151 16.1509,22.0291 C19.1399,22.0511 20.0179,20.7691 22.0269,21.4881 C24.3919,22.3371 25.3159,23.3921 27.1709,24.5401 C29.9429,26.2481 31.3449,26.2291 33.0259,25.1611 C33.0259,25.1611 34.6099,24.2071 34.1299,22.8691', + bottom: + 'M6.4121,23.752 L1.0791,34.822 C0.6321,35.565 0.5661,36.549 0.8771,37.084 C1.1851,37.623 1.7671,38.58 2.6191,38.58 L39.6821,38.58 C40.5341,38.58 41.1151,37.623 41.4251,37.084 C41.7341,36.549 41.6681,35.219 41.2231,34.479 L36.2291,26.217 C36.4201,27.445 34.3141,28.854 34.3141,28.854 C32.6091,29.992 30.3481,29.959 27.8371,28.975 C25.7441,28.152 24.4491,26.895 22.4571,25.955 C20.4671,25.012 19.3401,26.041 16.5901,26.281 C14.9531,26.424 13.9711,26.166 12.6221,25.324 C12.6221,25.324 9.9561,23.752 6.4601,23.752 L6.4121,23.752 Z', + }; + + // Variant 3 og 4 bruker samme geometri + const threePartMountain = { + top: 'M23.917,10.0605 L19.813,2.6985 C19.387,2.0665 18.906,1.7735 18.367,1.8185 C17.826,1.8645 17.4,2.2345 17.08,2.9285 L14.471,8.8895 L14.384,9.0545 C14.384,9.0545 16.84,8.2885 18.863,9.4345 C20.521,10.3725 21.144,11.3805 23.917,10.0605', + middle: + 'M34.2402,27.625 C34.2402,28.483 33.0472,29.218 33.0472,29.218 C31.3652,30.286 29.3092,30.762 26.6252,28.92 C24.6822,27.587 23.7362,26.562 21.4802,25.868 C19.4402,25.241 18.5942,26.431 15.6052,26.409 C13.6722,26.396 13.2832,25.827 12.2282,25.063 C12.2282,25.063 10.6742,23.747 8.6982,23.268 L8.0602,23.169 L12.9712,12.208 C12.9712,12.208 16.3302,11.472 18.5802,12.745 C20.4222,13.788 23.3102,15.342 25.7672,13.209 L34.2402,27.625', + bottom: + 'M5.9922,27.7842 L0.8322,39.2022 C0.3852,39.9442 0.3202,40.9292 0.6312,41.4642 C0.9392,42.0032 1.5212,42.9602 2.3732,42.9602 L39.4352,42.9602 C40.2872,42.9602 40.8692,42.0032 41.1782,41.4642 C41.4882,40.9292 41.4222,39.5982 40.9762,38.8582 L36.0102,30.6382 C35.9552,31.7022 34.0682,33.2332 34.0682,33.2332 C32.3632,34.3722 30.1012,34.3392 27.5902,33.3542 C25.4982,32.5322 24.2032,31.2742 22.2112,30.3352 C20.2212,29.3912 19.0942,30.4212 16.3442,30.6612 C14.7072,30.8042 13.7242,30.5462 12.3752,29.7042 C12.3752,29.7042 9.9982,27.7842 6.5022,27.7842 L5.9922,27.7842 Z', + }; + + // Bare fargeklassene endres mellom variantene + const colorMap = { + 1: { topClass: 'mountain-danger', bottomClass: 'mountain-safe' }, + 2: { topClass: 'mountain-safe', bottomClass: 'mountain-danger' }, + 3: { + topClass: 'mountain-danger', + middleClass: 'mountain-safe', + bottomClass: 'mountain-danger', + }, + 4: { + topClass: 'mountain-safe', + middleClass: 'mountain-danger', + bottomClass: 'mountain-safe', + }, + }; + + const geometry = variant <= 2 ? twoPartMountain : threePartMountain; + + return { ...geometry, ...colorMap[variant] }; + } + + private createArrowPath() { + return 'M11.92,8 L7.96,8 L7.96,16 L3.98,16 L3.98,8 L0,8 L5.96,0 Z'; + } + + render() { + const paths = this.createMountainPath(this.variant); + // Fill 3 og 4 trenger en y-offset på -6 for å vises riktig + const mountainYOffset = this.variant === 3 || this.variant === 4 ? -6 : 0; + + return html` + + ${this.renderMountain(paths)} + ${this.renderArrowsAndLabels()} + + `; + } + + private renderMountain(paths: MountainPaths) { + if (this.variant === 3 || this.variant === 4) { + return svg` + + + + `; + } else { + return svg` + + + `; + } + } + + private renderArrowsAndLabels() { + const mountainYOffset = this.variant === 3 || this.variant === 4 ? -6 : 0; + const mountainHeight = this.variant === 3 || this.variant === 4 ? 47 : 39; + const centerY = mountainHeight / 2 + mountainYOffset; + + const config = { + 1: { + groupHeight: 28, + arrows: [{ y: 0, rotation: 0 }], + texts: [{ y: 26, text: `${this.height1}m`, xOffset: 5.96, centered: true }], // Sentrert på pil + }, + 2: { + groupHeight: 28, + arrows: [{ y: 12, rotation: 180 }], + texts: [{ y: 5, text: `${this.height1}m`, xOffset: 5.96, centered: true }], // Sentrert på pil + }, + 3: { + groupHeight: 48, + arrows: [ + { y: 0, rotation: 0 }, + { y: 30, rotation: 180 }, + ], + texts: [ + { y: 20, text: `${this.maxHeight}m`, xOffset: 18, centered: false }, // Til høyre for pilene + { y: 38, text: `${this.minHeight}m`, xOffset: 18, centered: false }, + ], + }, + 4: { + groupHeight: 40, + arrows: [ + { y: 0, rotation: 180 }, + { y: 24, rotation: 0 }, + ], + texts: [{ y: 20, text: `${this.minHeight}-${this.maxHeight}m`, xOffset: 6, centered: false }], // Venstrejustert + }, + }; + + const { groupHeight, arrows, texts } = config[this.variant]; + + return svg` + + ${arrows.map((arrow) => this.renderArrow(arrow.y, arrow.rotation))} + ${texts.map((text) => this.renderText(text.y, text.text, text.xOffset, text.centered))} + + `; + } + + private renderArrow(y: number, rotation: number) { + const arrowPath = this.createArrowPath(); + + if (rotation === 0) { + return svg` + + + + `; + } else { + return svg` + + + + `; + } + } + + private renderText(y: number, text: string, xOffset: number, centered: boolean) { + const x = xOffset; + const className = centered ? 'height-label centered' : 'height-label'; + return svg`${text}`; + } + + private getAriaLabel(): string { + const prefix = this.lang === 'en' ? 'Exposed height' : 'Utsatt høyde'; + const between = this.lang === 'en' ? 'Between' : 'Mellom'; + const and = this.lang === 'en' ? 'and' : 'og'; + const meter = this.lang === 'en' ? 'meters' : 'meter'; + + switch (this.variant) { + case 1: + return `${prefix}: Over ${this.height1} ${meter}`; + case 2: + return `${prefix}: Under ${this.height1} ${meter}`; + case 3: + return `${prefix}: Under ${this.minHeight} ${meter} ${and} over ${this.maxHeight} ${meter}`; + case 4: + return `${prefix}: ${between} ${this.minHeight} ${and} ${this.maxHeight} ${meter}`; + default: + return ''; + } + } + + /** Returnerer den høyeste av de to høydene */ + private get maxHeight(): number { + return Math.max(this.height1, this.height2); + } + + /** Returnerer den laveste av de to høydene */ + private get minHeight(): number { + return Math.min(this.height1, this.height2); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'nve-exposed-height': NveExposedHeight; + } +} diff --git a/src/components/nve-exposed-height/nve-exposed-height.styles.ts b/src/components/nve-exposed-height/nve-exposed-height.styles.ts new file mode 100644 index 00000000..57057401 --- /dev/null +++ b/src/components/nve-exposed-height/nve-exposed-height.styles.ts @@ -0,0 +1,37 @@ +import { css } from 'lit'; + +export default css` + :host { + display: inline-block; + height: var(--exposed-height-size, 90px); + } + + .exposed-height { + width: 100%; + height: 100%; + } + + .mountain-danger { + fill: var(--exposed-height-affected-color, #d21523); + stroke: var(--exposed-height-affected-color, #d21523); + } + + .mountain-safe { + fill: var(--exposed-height-unaffected-color, #e3e3e3); + stroke: var(--exposed-height-unaffected-color, #e3e3e3); + } + + .arrow { + fill: var(--exposed-height-affected-color, #d21523); + } + + .height-label { + font-family: 'Source Sans 3', sans-serif; + font-weight: var(--font-weight-regular); + font-size: 0.7rem; + } + + .height-label.centered { + text-anchor: middle; + } +`; diff --git a/src/components/nve-exposed-height/nve-exposed-height.test.ts b/src/components/nve-exposed-height/nve-exposed-height.test.ts new file mode 100644 index 00000000..83deb874 --- /dev/null +++ b/src/components/nve-exposed-height/nve-exposed-height.test.ts @@ -0,0 +1,20 @@ +import { afterAll, describe, expect, it, } from 'vitest'; +import { fixture, fixtureCleanup } from '@open-wc/testing'; +import { html } from 'lit'; +import NveExposedHeight from './nve-exposed-height.component'; + +if (!customElements.get('nve-exposed-height')) { + customElements.define('nve-exposed-height', NveExposedHeight); +} + +describe('nve-exposed-height', () => { + afterAll(() => { + fixtureCleanup(); + }); + + it('is attached to the DOM', async () => { + const el = await fixture(html``); + expect(document.body.contains(el)).toBe(true); + }); +}); + \ No newline at end of file diff --git a/src/nve-designsystem.ts b/src/nve-designsystem.ts index 7f16f435..a0b9f1c1 100644 --- a/src/nve-designsystem.ts +++ b/src/nve-designsystem.ts @@ -16,6 +16,7 @@ export { default as NveDialog } from './components/nve-dialog/nve-dialog.compone export { default as NveDivider } from './components/nve-divider/nve-divider.component'; export { default as NveDrawer } from './components/nve-drawer/nve-drawer.component'; export { default as NveDropdown } from './components/nve-dropdown/nve-dropdown.component'; +export { default as NveExposedHeight } from './components/nve-exposed-height/nve-exposed-height.component'; export { default as NveHeading } from './components/nve-heading/nve-heading.component'; export { default as NveIcon } from './components/nve-icon/nve-icon.component'; export { default as NveInput } from './components/nve-input/nve-input.component'; @@ -45,3 +46,4 @@ export { default as NveTag } from './components/nve-tag/nve-tag.component'; export { default as NveTextarea } from './components/nve-textarea/nve-textarea.component'; export { default as NveTooltip } from './components/nve-tooltip/nve-tooltip.component'; export { default as NveWarningLevel } from './components/nve-warning-level/nve-warning-level.component'; + \ No newline at end of file From 09256cbfe5cb2f750037831c26e376cced1c7163 Mon Sep 17 00:00:00 2001 From: gruble Date: Fri, 12 Jun 2026 12:38:53 +0200 Subject: [PATCH 2/6] =?UTF-8?q?Justert=20p=C3=A5=20st=C3=B8rrelse,=20plass?= =?UTF-8?q?ering=20av=20tekst=20og=20piler.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lagt til tester og forbedret dok/test --- doc-site/components/Komponentoversikt.md | 7 + doc-site/components/nve-exposed-height.md | 36 ++-- .../nve-exposed-height.component.ts | 32 ++-- .../nve-exposed-height.styles.ts | 6 +- .../nve-exposed-height.test.ts | 162 +++++++++++++++++- 5 files changed, 203 insertions(+), 40 deletions(-) diff --git a/doc-site/components/Komponentoversikt.md b/doc-site/components/Komponentoversikt.md index a2b09374..42611421 100644 --- a/doc-site/components/Komponentoversikt.md +++ b/doc-site/components/Komponentoversikt.md @@ -167,6 +167,13 @@ nodeId er ID til komponent-sida i Figma. Den ligger som en parameter i URL'en ti statusDesign: 'Skal revideres', statusCode: 'Ferdig' }, + { + name: 'nve-exposed-height', + nodeId: undefined, + description: undefined, + statusDesign: undefined, + statusCode: 'Ferdig' + }, { name: 'nve-icon', nodeId: undefined, diff --git a/doc-site/components/nve-exposed-height.md b/doc-site/components/nve-exposed-height.md index d6de385c..bb29fb16 100644 --- a/doc-site/components/nve-exposed-height.md +++ b/doc-site/components/nve-exposed-height.md @@ -3,10 +3,6 @@ layout: component outline: [2, 3] --- -# Utsatt høyde - -Viser utsatt høyde for et skredproblem. Komponenten bruker et fjellsymbol med piler og høydetekst for å indikere hvilke høyder som er utsatt. - ```html @@ -15,9 +11,7 @@ Viser utsatt høyde for et skredproblem. Komponenten bruker et fjellsymbol med p -Komponenten er skalerbar og standard høyde er 90px. Du kan endre størrelsen med CSS-variabelen `--exposed-height-size`. - -## Eksempler +## Eksempler på bruk ### Varianter @@ -29,8 +23,11 @@ Bruk `variant` for å angi om skredproblemet gjelder: 4. mellom to høyder Variant 1 er standard. -For variant 1 og 2 må du sende inn `height1`. -For variant 3 og 4 må du sende inn både `height1` og `height2`. +I tillegg må du angi høyde: + +- For variant 1 og 2 må du sende inn `height1`. +- For variant 3 og 4 må du sende inn både `height1` og `height2`. + Komponenten finner selv ut om `height1` eller `height2` er høyest. @@ -47,26 +44,27 @@ For variant 3 og 4 må du sende inn både `height1` og `height2`. Her er noen eksempler på feil bruk: - + ```html - utsatt høyde blir over 0m om variant og height1 ikke angis - variant 2 mangler height1 - variant 3 må ha både height 1 og height2 - variant 4 må ha både height 1 og height2 + variant 1 +og 2 mangler height1 + + variant 3 og 4 må ha både height 1 og height2 ``` ### Størrelse -Endre høyden med CSS-variabelen `--exposed-height-size`. Standard høyde er 90px. +Endre bredden med CSS-variabelen `--exposed-height-width`. Standard bredde er 150px. ```html - 120px høyde -90px høyde + + + ``` @@ -78,13 +76,13 @@ Bruk `lang="en"` for å vise aria-label på engelsk. Norsk er standard. ```html -Engelsk: +en: -Norsk: +no: diff --git a/src/components/nve-exposed-height/nve-exposed-height.component.ts b/src/components/nve-exposed-height/nve-exposed-height.component.ts index ebdf8e9b..b5ad0dbd 100644 --- a/src/components/nve-exposed-height/nve-exposed-height.component.ts +++ b/src/components/nve-exposed-height/nve-exposed-height.component.ts @@ -13,10 +13,11 @@ interface MountainPaths { } /** - * Visualiserer utsatt høyde for skredproblemer. - * Viser et fjellsymbol med piler og høydeindikasjon. + * Visualisering av utsatt høyde for et skredproblem. + * Viser et fjell med fargelegging av utsatt høyde i tillegg til piler og høydegrenser. * @cssproperty --exposed-height-affected-color - Farge på utsatte høyde. Standard er #d21523. * @cssproperty --exposed-height-unaffected-color - Farge på ikke-utsatt høyde. Standard er #e3e3e3. + * @cssproperty --exposed-height-width - Bredde på hele komponenten. Standard er 150px. */ @customElement('nve-exposed-height') export default class NveExposedHeight extends LitElement implements INveComponent { @@ -36,6 +37,7 @@ export default class NveExposedHeight extends LitElement implements INveComponen static styles = [styles]; + // grafikk for fjellet private createMountainPath(variant: 1 | 2 | 3 | 4): MountainPaths { // Variant 1 og 2 bruker samme geometri const twoPartMountain = { @@ -83,10 +85,18 @@ export default class NveExposedHeight extends LitElement implements INveComponen // Fill 3 og 4 trenger en y-offset på -6 for å vises riktig const mountainYOffset = this.variant === 3 || this.variant === 4 ? -6 : 0; + const isThreePart = this.variant === 3 || this.variant === 4; + const vbY = isThreePart ? -6 : -2; + const vbH = isThreePart ? 48 : 46; + return html` + ${arrows.map((arrow) => this.renderArrow(arrow.y, arrow.rotation))} ${texts.map((text) => this.renderText(text.y, text.text, text.xOffset, text.centered))} diff --git a/src/components/nve-exposed-height/nve-exposed-height.styles.ts b/src/components/nve-exposed-height/nve-exposed-height.styles.ts index 57057401..28229367 100644 --- a/src/components/nve-exposed-height/nve-exposed-height.styles.ts +++ b/src/components/nve-exposed-height/nve-exposed-height.styles.ts @@ -3,12 +3,12 @@ import { css } from 'lit'; export default css` :host { display: inline-block; - height: var(--exposed-height-size, 90px); + width: var(--exposed-height-width, 150px); } .exposed-height { width: 100%; - height: 100%; + height: auto; /* auto = maintains aspect ratio from intrinsic width/height */ } .mountain-danger { @@ -28,7 +28,7 @@ export default css` .height-label { font-family: 'Source Sans 3', sans-serif; font-weight: var(--font-weight-regular); - font-size: 0.7rem; + font-size: 0.67rem; } .height-label.centered { diff --git a/src/components/nve-exposed-height/nve-exposed-height.test.ts b/src/components/nve-exposed-height/nve-exposed-height.test.ts index 83deb874..c7c39169 100644 --- a/src/components/nve-exposed-height/nve-exposed-height.test.ts +++ b/src/components/nve-exposed-height/nve-exposed-height.test.ts @@ -1,20 +1,168 @@ -import { afterAll, describe, expect, it, } from 'vitest'; +import { afterAll, describe, expect, it } from 'vitest'; import { fixture, fixtureCleanup } from '@open-wc/testing'; import { html } from 'lit'; import NveExposedHeight from './nve-exposed-height.component'; - + if (!customElements.get('nve-exposed-height')) { customElements.define('nve-exposed-height', NveExposedHeight); } describe('nve-exposed-height', () => { - afterAll(() => { - fixtureCleanup(); - }); - + afterAll(() => fixtureCleanup()); + it('is attached to the DOM', async () => { const el = await fixture(html``); expect(document.body.contains(el)).toBe(true); }); + + it('has correct default properties', async () => { + const el = await fixture(html``); + expect(el.variant).toBe(1); + expect(el.height1).toBe(0); + expect(el.height2).toBe(0); + expect(el.lang).toBe('no'); + }); + + // --- aria-label --- + + it('har korrekt aria-label for variant 1 på norsk', async () => { + const el = await fixture( + html`` + ); + const svg = el.shadowRoot?.querySelector('svg'); + expect(svg?.getAttribute('aria-label')).toBe('Utsatt høyde: Over 1000 meter'); + }); + + it('har korrekt aria-label for variant 2 på norsk', async () => { + const el = await fixture( + html`` + ); + const svg = el.shadowRoot?.querySelector('svg'); + expect(svg?.getAttribute('aria-label')).toBe('Utsatt høyde: Under 1000 meter'); + }); + + it('har korrekt aria-label for variant 3 på norsk', async () => { + const el = await fixture( + html`` + ); + const svg = el.shadowRoot?.querySelector('svg'); + expect(svg?.getAttribute('aria-label')).toBe('Utsatt høyde: Under 800 meter og over 1200 meter'); + }); + + it('har korrekt aria-label for variant 4 på norsk', async () => { + const el = await fixture( + html`` + ); + const svg = el.shadowRoot?.querySelector('svg'); + expect(svg?.getAttribute('aria-label')).toBe('Utsatt høyde: Mellom 800 og 1200 meter'); + }); + + it('has correct aria-label for variant 1 in English', async () => { + const el = await fixture( + html`` + ); + const svg = el.shadowRoot?.querySelector('svg'); + expect(svg?.getAttribute('aria-label')).toBe('Exposed height: Over 1000 meters'); + }); + + // --- fjell-grafikk: riktig antall paths og klasser --- + + it('variant 1 har to fjell-paths: topp=danger, bunn=safe', async () => { + const el = await fixture( + html`` + ); + const mountainPaths = el.shadowRoot?.querySelectorAll('svg > g:first-child path'); + expect(mountainPaths?.length).toBe(2); + expect(mountainPaths?.[0].classList.contains('mountain-danger')).toBe(true); + expect(mountainPaths?.[1].classList.contains('mountain-safe')).toBe(true); + }); + + it('variant 2 har to fjell-paths: topp=safe, bunn=danger', async () => { + const el = await fixture( + html`` + ); + const mountainPaths = el.shadowRoot?.querySelectorAll('svg > g:first-child path'); + expect(mountainPaths?.length).toBe(2); + expect(mountainPaths?.[0].classList.contains('mountain-safe')).toBe(true); + expect(mountainPaths?.[1].classList.contains('mountain-danger')).toBe(true); + }); + + it('variant 3 har tre fjell-paths: topp=danger, midtre=safe, bunn=danger', async () => { + const el = await fixture( + html`` + ); + const mountainPaths = el.shadowRoot?.querySelectorAll('svg > g:first-child path'); + expect(mountainPaths?.length).toBe(3); + expect(mountainPaths?.[0].classList.contains('mountain-danger')).toBe(true); + expect(mountainPaths?.[1].classList.contains('mountain-safe')).toBe(true); + expect(mountainPaths?.[2].classList.contains('mountain-danger')).toBe(true); + }); + + it('variant 4 har tre fjell-paths: topp=safe, midtre=danger, bunn=safe', async () => { + const el = await fixture( + html`` + ); + const mountainPaths = el.shadowRoot?.querySelectorAll('svg > g:first-child path'); + expect(mountainPaths?.length).toBe(3); + expect(mountainPaths?.[0].classList.contains('mountain-safe')).toBe(true); + expect(mountainPaths?.[1].classList.contains('mountain-danger')).toBe(true); + expect(mountainPaths?.[2].classList.contains('mountain-safe')).toBe(true); + }); + + // --- piler --- + + it('variant 1 og 2 viser én pil', async () => { + for (const variant of [1, 2] as const) { + const el = await fixture( + html`` + ); + const arrows = el.shadowRoot?.querySelectorAll('.arrow'); + expect(arrows?.length).toBe(1); + } + }); + + it('variant 3 og 4 viser to piler', async () => { + for (const variant of [3, 4] as const) { + const el = await fixture( + html`` + ); + const arrows = el.shadowRoot?.querySelectorAll('.arrow'); + expect(arrows?.length).toBe(2); + } + }); + + // --- høydetekst --- + + it('variant 1 har riktig tekst', async () => { + const el = await fixture( + html`` + ); + const labels = el.shadowRoot?.querySelectorAll('text.height-label'); + expect(labels?.[0].textContent).toBe('1500m'); + }); + + it('variant 2 har riktig tekst', async () => { + const el = await fixture( + html`` + ); + const labels = el.shadowRoot?.querySelectorAll('text.height-label'); + expect(labels?.[0].textContent).toBe('1500m'); + }); + + it('variant 3 har høyeste verdi øverst uansett rekkefølge på height1/height2', async () => { + const el = await fixture( + html`` + ); + const labels = el.shadowRoot?.querySelectorAll('text.height-label'); + expect(labels?.[0].textContent).toBe('1200m'); // max øverst + expect(labels?.[1].textContent).toBe('800m'); // min nederst + }); + + it('variant 4 har riktig tekst', async () => { + const el = await fixture( + html`` + ); + const labels = el.shadowRoot?.querySelectorAll('text.height-label'); + expect(labels?.[0].textContent).toBe('800-1200m'); + }); }); - \ No newline at end of file From 09753a6a85629db21ff7813dce846faf231c6a13 Mon Sep 17 00:00:00 2001 From: gruble Date: Fri, 12 Jun 2026 15:18:50 +0200 Subject: [PATCH 3/6] Fikset etter kommentarer fra copilot --- doc-site/components/nve-exposed-height.md | 9 +- .../nve-exposed-height/AvalancheRose.js | 249 ------------------ .../nve-exposed-height.component.ts | 5 +- src/nve-designsystem.ts | 1 - 4 files changed, 6 insertions(+), 258 deletions(-) delete mode 100644 src/components/nve-exposed-height/AvalancheRose.js diff --git a/doc-site/components/nve-exposed-height.md b/doc-site/components/nve-exposed-height.md index bb29fb16..feb90a13 100644 --- a/doc-site/components/nve-exposed-height.md +++ b/doc-site/components/nve-exposed-height.md @@ -47,10 +47,11 @@ Her er noen eksempler på feil bruk: ```html - variant 1 -og 2 mangler height1 + +

variant 1 og 2 mangler height1

- variant 3 og 4 må ha både height 1 og height2 + +

variant 3 og 4 må ha både height 1 og height2

```
@@ -99,7 +100,7 @@ Bruk css-variablene `--exposed-height-affected-color` og `--exposed-height-unaff ```html - + diff --git a/src/components/nve-exposed-height/AvalancheRose.js b/src/components/nve-exposed-height/AvalancheRose.js deleted file mode 100644 index b71dae56..00000000 --- a/src/components/nve-exposed-height/AvalancheRose.js +++ /dev/null @@ -1,249 +0,0 @@ -(function () { - - function drawAvalacheRose(element) { - - var validExpositions = $(element).data("valid-expositions").toString(); - var exposedHeight1 = $(element).data("exposed-height1"); - var exposedHeight2 = $(element).data("exposed-height2"); - var exposedHeightFill = $(element).data("exposed-height-fill"); - - var container = document.createElementNS('http://www.w3.org/2000/svg', 'g'); - container.setAttribute('transform', 'scale(' + 1 + ')'); - - var radius = 36; - var cx = radius + 0.25 * radius; - var cy = cx; - - container.appendChild(createCake(radius, cx, cy, validExpositions.split(''))); - - container.appendChild(createLabel(cx, cy - radius, 0.2 * radius, 'N')); - container.appendChild(createLabel(cx + radius, cy, 0.2 * radius, 'Ø')); - container.appendChild(createLabel(cx, cy + radius, 0.2 * radius, 'S')); - container.appendChild(createLabel(cx - radius, cy, 0.2 * radius, 'V')); - - if (exposedHeightFill === 1) { - container.appendChild(createMountain(cx + 1.3 * radius, cy, radius, exposedHeightFill)); - container.appendChild(createUpArrow(cx + 2.8 * radius, cy + radius - 30, 11.92, 16)); - var label = createHeightLabel(cx + 2.8 * radius + 11.92 / 2, cy + radius, radius, exposedHeight1 + 'm'); - label.classList.add('centered'); - container.appendChild(label); - } else if (exposedHeightFill === 2) { - container.appendChild(createMountain(cx + 1.3 * radius, cy, radius, exposedHeightFill)); - container.appendChild(createDownArrow(cx + 2.8 * radius, cy + radius - 16, 11.92, 16)); - var label = createHeightLabel(cx + 2.8 * radius + 11.92 / 2, cy + 16, radius, exposedHeight1 + 'm'); - label.classList.add('centered'); - container.appendChild(label); - } else if (exposedHeightFill === 3) { - container.appendChild(createMountain(cx + 1.3 * radius, cy - 6, radius, exposedHeightFill)); - container.appendChild(createUpArrow(cx + 2.7 * radius, cy + radius - 40, 11.92, 16)); - container.appendChild(createDownArrow(cx + 2.7 * radius, cy + radius - 16, 11.92, 16)); - container.appendChild(createHeightLabel(cx + 2.8 * radius + 16, cy + 10, radius, exposedHeight1 + 'm')); - container.appendChild(createHeightLabel(cx + 2.8 * radius + 16, cy + radius - 8, radius, exposedHeight2 + 'm')); - } else if (exposedHeightFill === 4) { - container.appendChild(createMountain(cx + 1.3 * radius, cy - 6, radius, exposedHeightFill)); - container.appendChild(createDownArrow(cx + 2.7 * radius, cy + radius - 48, 11.92, 16)); - container.appendChild(createUpArrow(cx + 2.7 * radius, cy + radius - 16, 11.92, 16)); - container.appendChild(createHeightLabel(cx + 2.7 * radius, cy + 16, radius, exposedHeight2 + '-' + exposedHeight1 + 'm')); - } - - var svgElement = element.children().get(0); - - svgElement.appendChild(container); - } - - function createCake(radius, cx, cy, activeSectors) { - var n = activeSectors.length; - var sectorAngle = 360 / n; - var x1 = cx; - var y1 = cy - radius; - var x2 = cx + Math.cos(sectorAngle * Math.PI / 180) * radius; - var y2 = cy - Math.sin(sectorAngle * Math.PI / 180) * radius; - var container = document.createElementNS('http://www.w3.org/2000/svg', 'g'); - - for (var i = 0; i < n; i++) { - var sector = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - sector.setAttribute('d', - 'M ' + x1 + ' ' + y1 + ' ' + - 'A ' + radius + ' ' + radius + ' 0 0 1 ' + x2 + ' ' + y2 + ' ' + - 'L ' + cx + ' ' + cy + ' ' + - 'Z'); - sector.setAttribute('transform', 'rotate(' + (-22.5 + i * sectorAngle) + ' ' + cx + ' ' + cy + ')'); - sector.classList.add('sector'); - if (activeSectors[i] == 1) { - sector.classList.add('active'); - } - container.appendChild(sector); - } - - var outline = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); - outline.setAttribute('cx', cx); - outline.setAttribute('cy', cy); - outline.setAttribute('r', radius); - outline.setAttribute('fill', 'none'); - outline.setAttribute('stroke-width', '1'); - outline.classList.add('circle-outline'); - container.appendChild(outline); - - return container; - } - - function createLabel(x, y, r, text) { - var fontSize = 1.1 * r; - var container = document.createElementNS('http://www.w3.org/2000/svg', 'g'); - - var svgCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); - svgCircle.setAttribute('cx', x); - svgCircle.setAttribute('cy', y); - svgCircle.setAttribute('r', r); - svgCircle.classList.add('circle-label'); - container.appendChild(svgCircle); - - var svgText = document.createElementNS('http://www.w3.org/2000/svg', 'text'); - svgText.setAttribute('x', x); - svgText.setAttribute('y', y); - svgText.setAttribute('dy', (0.4 * fontSize) + ''); - svgText.setAttribute('font-size', fontSize + ''); - svgText.classList.add('centered'); - svgText.textContent = text; - container.appendChild(svgText); - - return container; - } - - function createMountain(x, y, width, exposedHeightFill) { - var container = document.createElementNS('http://www.w3.org/2000/svg', 'g'); - - if (exposedHeightFill == 1) { - - var top = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - top.setAttribute('d', 'M34.1299,22.8691 L20.1809,1.0741 C19.7079,0.3711 19.1749,0.0461 18.5749,0.0961 C17.9749,0.1461 17.4999,0.5581 17.1459,1.3301 L8.5139,18.9511 C10.8769,18.8161 12.7759,20.6831 12.7759,20.6831 C13.8629,21.6761 14.2179,22.0151 16.1509,22.0291 C19.1399,22.0511 20.0179,20.7691 22.0269,21.4881 C24.3919,22.3371 25.3159,23.3921 27.1709,24.5401 C29.9429,26.2481 31.3449,26.2291 33.0259,25.1611 C33.0259,25.1611 34.6099,24.2071 34.1299,22.8691'); - top.setAttribute('fill', '#d21523'); - top.setAttribute('stroke-width', '1'); - top.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); - container.appendChild(top); - - var bottom = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - bottom.setAttribute('d', 'M6.4121,23.752 L1.0791,34.822 C0.6321,35.565 0.5661,36.549 0.8771,37.084 C1.1851,37.623 1.7671,38.58 2.6191,38.58 L39.6821,38.58 C40.5341,38.58 41.1151,37.623 41.4251,37.084 C41.7341,36.549 41.6681,35.219 41.2231,34.479 L36.2291,26.217 C36.4201,27.445 34.3141,28.854 34.3141,28.854 C32.6091,29.992 30.3481,29.959 27.8371,28.975 C25.7441,28.152 24.4491,26.895 22.4571,25.955 C20.4671,25.012 19.3401,26.041 16.5901,26.281 C14.9531,26.424 13.9711,26.166 12.6221,25.324 C12.6221,25.324 9.9561,23.752 6.4601,23.752 L6.4121,23.752 Z'); - bottom.setAttribute('fill', '#E3E3E3'); - bottom.setAttribute('stroke-width', '1'); - bottom.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); - container.appendChild(bottom); - - } else if (exposedHeightFill == 2) { - - var top = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - top.setAttribute('d', 'M34.1299,23.0034 L20.1809,1.2094 C19.7079,0.5054 19.1749,0.1804 18.5749,0.2304 C17.9749,0.2814 17.4999,0.6934 17.1459,1.4644 L8.5139,19.0864 C10.8769,18.9514 12.7759,20.8184 12.7759,20.8184 C13.8629,21.8104 14.2179,22.1504 16.1509,22.1644 C19.1399,22.1854 20.0179,20.9044 22.0269,21.6234 C24.3919,22.4714 25.3159,23.5274 27.1709,24.6744 C29.9429,26.3824 31.3449,26.3644 33.0259,25.2954 C33.0259,25.2954 34.6099,24.3414 34.1299,23.0034 L34.1299,23.0034 Z'); - top.setAttribute('fill', '#E3E3E3'); - top.setAttribute('stroke-width', '1'); - top.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); - container.appendChild(top); - - var bottom = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - bottom.setAttribute('d', 'M6.4121,23.8867 L1.0791,34.9567 C0.6321,35.6997 0.5661,36.6837 0.8771,37.2187 C1.1851,37.7577 1.7671,38.7147 2.6191,38.7147 L39.6821,38.7147 C40.5341,38.7147 41.1151,37.7577 41.4251,37.2187 C41.7341,36.6837 41.6681,35.3537 41.2231,34.6137 L36.2291,26.3517 C36.4201,27.5797 34.3141,28.9887 34.3141,28.9887 C32.6091,30.1267 30.3481,30.0937 27.8371,29.1097 C25.7441,28.2867 24.4491,27.0297 22.4571,26.0897 C20.4671,25.1467 19.3401,26.1757 16.5901,26.4157 C14.9531,26.5587 13.9711,26.3007 12.6221,25.4587 C12.6221,25.4587 9.9561,23.8867 6.4601,23.8867 L6.4121,23.8867 Z'); - bottom.setAttribute('fill', '#d21523'); - bottom.setAttribute('stroke-width', '1'); - bottom.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); - container.appendChild(bottom); - - } else if (exposedHeightFill == 3) { - - var top = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - top.setAttribute('d', 'M23.917,10.0605 L19.813,2.6985 C19.387,2.0665 18.906,1.7735 18.367,1.8185 C17.826,1.8645 17.4,2.2345 17.08,2.9285 L14.471,8.8895 L14.384,9.0545 C14.384,9.0545 16.84,8.2885 18.863,9.4345 C20.521,10.3725 21.144,11.3805 23.917,10.0605'); - top.setAttribute('fill', '#d21523'); - top.setAttribute('stroke-width', '1'); - top.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); - container.appendChild(top); - - var middle = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - middle.setAttribute('d', 'M34.2402,27.625 C34.2402,28.483 33.0472,29.218 33.0472,29.218 C31.3652,30.286 29.3092,30.762 26.6252,28.92 C24.6822,27.587 23.7362,26.562 21.4802,25.868 C19.4402,25.241 18.5942,26.431 15.6052,26.409 C13.6722,26.396 13.2832,25.827 12.2282,25.063 C12.2282,25.063 10.6742,23.747 8.6982,23.268 L8.0602,23.169 L12.9712,12.208 C12.9712,12.208 16.3302,11.472 18.5802,12.745 C20.4222,13.788 23.3102,15.342 25.7672,13.209 L34.2402,27.625'); - middle.setAttribute('fill', '#E3E3E3'); - middle.setAttribute('stroke-width', '1'); - middle.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); - container.appendChild(middle); - - var bottom = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - bottom.setAttribute('d', 'M5.9922,27.7842 L0.8322,39.2022 C0.3852,39.9442 0.3202,40.9292 0.6312,41.4642 C0.9392,42.0032 1.5212,42.9602 2.3732,42.9602 L39.4352,42.9602 C40.2872,42.9602 40.8692,42.0032 41.1782,41.4642 C41.4882,40.9292 41.4222,39.5982 40.9762,38.8582 L36.0102,30.6382 C35.9552,31.7022 34.0682,33.2332 34.0682,33.2332 C32.3632,34.3722 30.1012,34.3392 27.5902,33.3542 C25.4982,32.5322 24.2032,31.2742 22.2112,30.3352 C20.2212,29.3912 19.0942,30.4212 16.3442,30.6612 C14.7072,30.8042 13.7242,30.5462 12.3752,29.7042 C12.3752,29.7042 9.9982,27.7842 6.5022,27.7842 L5.9922,27.7842 Z'); - bottom.setAttribute('fill', '#d21523'); - bottom.setAttribute('stroke-width', '1'); - bottom.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); - container.appendChild(bottom); - - } else if (exposedHeightFill == 4) { - - var top = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - top.setAttribute('d', 'M23.917,14.0537 L19.813,6.6917 C19.387,6.0597 18.906,5.7667 18.367,5.8117 C17.826,5.8577 17.4,6.2277 17.08,6.9217 L14.471,12.8827 L14.384,13.0477 C14.384,13.0477 16.84,12.2817 18.863,13.4277 C20.521,14.3657 21.144,15.3737 23.917,14.0537'); - top.setAttribute('fill', '#E3E3E3'); - top.setAttribute('stroke-width', '1'); - top.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); - container.appendChild(top); - - var middle = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - middle.setAttribute('d', 'M34.2402,31.6182 C34.2402,32.4762 33.0472,33.2112 33.0472,33.2112 C31.3652,34.2792 29.3092,34.7552 26.6252,32.9132 C24.6822,31.5802 23.7362,30.5552 21.4802,29.8612 C19.4402,29.2342 18.5942,30.4242 15.6052,30.4022 C13.6722,30.3892 13.2832,29.8202 12.2282,29.0562 C12.2282,29.0562 10.6742,27.7402 8.6982,27.2612 L8.0602,27.1622 L12.9712,16.2012 C12.9712,16.2012 16.3302,15.4652 18.5802,16.7382 C20.4222,17.7812 23.3102,19.3352 25.7672,17.2022 L34.2402,31.6182'); - middle.setAttribute('fill', '#d21523'); - middle.setAttribute('stroke-width', '1'); - middle.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); - container.appendChild(middle); - - var bottom = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - bottom.setAttribute('d', 'M5.9922,31.7773 L0.8322,43.1953 C0.3852,43.9373 0.3202,44.9223 0.6312,45.4573 C0.9392,45.9963 1.5212,46.9533 2.3732,46.9533 L39.4352,46.9533 C40.2872,46.9533 40.8692,45.9963 41.1782,45.4573 C41.4882,44.9223 41.4222,43.5913 40.9762,42.8513 L36.0102,34.6313 C35.9552,35.6953 34.0682,37.2263 34.0682,37.2263 C32.3632,38.3653 30.1012,38.3323 27.5902,37.3473 C25.4982,36.5253 24.2032,35.2673 22.2112,34.3283 C20.2212,33.3843 19.0942,34.4143 16.3442,34.6543 C14.7072,34.7973 13.7242,34.5393 12.3752,33.6973 C12.3752,33.6973 9.9982,31.7773 6.5022,31.7773 L5.9922,31.7773 Z'); - bottom.setAttribute('fill', '#E3E3E3'); - bottom.setAttribute('stroke-width', '1'); - bottom.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); - container.appendChild(bottom); - - } - - return container; - } - - function createHeightLabel(x, y, r, text) { - var fontSize = 0.4 * r; - var svgText = document.createElementNS('http://www.w3.org/2000/svg', 'text'); - svgText.setAttribute('x', x); - svgText.setAttribute('y', y); - //svgText.setAttribute("dy", (0.4 * fontSize) + ""); - svgText.setAttribute('font-size', fontSize + ''); - svgText.textContent = text; - return svgText; - } - - function createUpArrow(x, y, width, height) { - var container = document.createElementNS('http://www.w3.org/2000/svg', 'g'); - var arrow = createArrow(width, height); - container.appendChild(arrow); - container.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); - return container; - } - - function createDownArrow(x, y, width, height) { - var container = document.createElementNS('http://www.w3.org/2000/svg', 'g'); - var arrow = createArrow(width, height); - arrow.setAttribute('transform', 'rotate(180 ' + (width / 2) + ' ' + (height / 2) + ')'); - - container.appendChild(arrow); - container.setAttribute('transform', 'translate(' + x + ', ' + y + ')'); - return container; - } - - function createArrow(width, height) { - var shaftWidth = 0.3334 * width; - var shaftHeight = 0.5 * height; - - var arrow = document.createElementNS('http://www.w3.org/2000/svg', 'polygon'); - arrow.setAttribute('points', - width + ' ' + (height - shaftHeight) + ' ' + - (width / 2 + shaftWidth / 2) + ' ' + (height - shaftHeight) + ' ' + - (width / 2 + shaftWidth / 2) + ' ' + (height) + ' ' + - (width / 2 - shaftWidth / 2) + ' ' + (height) + ' ' + - (width / 2 - shaftWidth / 2) + ' ' + (height - shaftHeight) + ' ' + - '0 ' + (height - shaftHeight) + ' ' + - (width / 2) + ' 0'); - arrow.classList.add('arrow'); - - return arrow; - } - - $("nve-avalanche-rose").each(function () { - drawAvalacheRose($(this)); - }); -})(); \ No newline at end of file diff --git a/src/components/nve-exposed-height/nve-exposed-height.component.ts b/src/components/nve-exposed-height/nve-exposed-height.component.ts index b5ad0dbd..4d1fec84 100644 --- a/src/components/nve-exposed-height/nve-exposed-height.component.ts +++ b/src/components/nve-exposed-height/nve-exposed-height.component.ts @@ -93,13 +93,10 @@ export default class NveExposedHeight extends LitElement implements INveComponen ${this.renderMountain(paths)} ${this.renderArrowsAndLabels()} diff --git a/src/nve-designsystem.ts b/src/nve-designsystem.ts index a0b9f1c1..d99de8f2 100644 --- a/src/nve-designsystem.ts +++ b/src/nve-designsystem.ts @@ -46,4 +46,3 @@ export { default as NveTag } from './components/nve-tag/nve-tag.component'; export { default as NveTextarea } from './components/nve-textarea/nve-textarea.component'; export { default as NveTooltip } from './components/nve-tooltip/nve-tooltip.component'; export { default as NveWarningLevel } from './components/nve-warning-level/nve-warning-level.component'; - \ No newline at end of file From fda52f0f2e604716c2c7b0a6a6078d3446d19578 Mon Sep 17 00:00:00 2001 From: gruble Date: Fri, 12 Jun 2026 15:24:41 +0200 Subject: [PATCH 4/6] =?UTF-8?q?Sm=C3=A5plukk=20i=20kodeeksempler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc-site/components/nve-exposed-height.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc-site/components/nve-exposed-height.md b/doc-site/components/nve-exposed-height.md index feb90a13..2b40b1e0 100644 --- a/doc-site/components/nve-exposed-height.md +++ b/doc-site/components/nve-exposed-height.md @@ -6,7 +6,7 @@ outline: [2, 3] ```html - + ``` @@ -47,7 +47,8 @@ Her er noen eksempler på feil bruk: ```html - + +

variant 1 og 2 mangler height1

From 9fdf4857fbe5256932d32f7c8ee5691fb7bc8942 Mon Sep 17 00:00:00 2001 From: gruble Date: Fri, 12 Jun 2026 15:55:11 +0200 Subject: [PATCH 5/6] =?UTF-8?q?Gjort=20spr=C3=A5kvalg=20mer=20brukervennli?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc-site/components/nve-exposed-height.md | 6 +++--- .../nve-exposed-height.component.ts | 16 ++++++++++------ .../nve-exposed-height.test.ts | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/doc-site/components/nve-exposed-height.md b/doc-site/components/nve-exposed-height.md index 2b40b1e0..4130871b 100644 --- a/doc-site/components/nve-exposed-height.md +++ b/doc-site/components/nve-exposed-height.md @@ -73,7 +73,7 @@ Endre bredden med CSS-variabelen `--exposed-height-width`. Standard bredde er 15 ### Språk -Bruk `lang="en"` for å vise aria-label på engelsk. Norsk er standard. +Sett `lang` til noe som starter på 'en' for å vise aria-label på engelsk. Norsk er standard. @@ -81,8 +81,8 @@ Bruk `lang="en"` for å vise aria-label på engelsk. Norsk er standard. en: - - + + no: diff --git a/src/components/nve-exposed-height/nve-exposed-height.component.ts b/src/components/nve-exposed-height/nve-exposed-height.component.ts index 4d1fec84..e31f7d45 100644 --- a/src/components/nve-exposed-height/nve-exposed-height.component.ts +++ b/src/components/nve-exposed-height/nve-exposed-height.component.ts @@ -30,13 +30,17 @@ export default class NveExposedHeight extends LitElement implements INveComponen /** Andre høydeverdi i meter (for variant 3 og 4) */ @property({ type: Number }) height2: number = 0; - /** Språk for aria-label: 'no' for norsk, 'en' for engelsk. Norsk er standard. */ - @property({ type: String }) lang: 'no' | 'en' = 'no'; + /** Språk for aria-label. Blir satt til engelsk om lang starter på 'en'. Standard er norsk. */ + @property({ type: String }) lang: string = 'no'; @property({ type: String }) testId: string | undefined = undefined; static styles = [styles]; + private isEnglish() { + return this.lang.toLowerCase().startsWith('en'); + } + // grafikk for fjellet private createMountainPath(variant: 1 | 2 | 3 | 4): MountainPaths { // Variant 1 og 2 bruker samme geometri @@ -191,10 +195,10 @@ export default class NveExposedHeight extends LitElement implements INveComponen } private getAriaLabel(): string { - const prefix = this.lang === 'en' ? 'Exposed height' : 'Utsatt høyde'; - const between = this.lang === 'en' ? 'Between' : 'Mellom'; - const and = this.lang === 'en' ? 'and' : 'og'; - const meter = this.lang === 'en' ? 'meters' : 'meter'; + const prefix = this.isEnglish() ? 'Exposed height' : 'Utsatt høyde'; + const between = this.isEnglish() ? 'Between' : 'Mellom'; + const and = this.isEnglish() ? 'and' : 'og'; + const meter = this.isEnglish() ? 'meters' : 'meter'; switch (this.variant) { case 1: diff --git a/src/components/nve-exposed-height/nve-exposed-height.test.ts b/src/components/nve-exposed-height/nve-exposed-height.test.ts index c7c39169..c6c43d9e 100644 --- a/src/components/nve-exposed-height/nve-exposed-height.test.ts +++ b/src/components/nve-exposed-height/nve-exposed-height.test.ts @@ -59,7 +59,7 @@ describe('nve-exposed-height', () => { it('has correct aria-label for variant 1 in English', async () => { const el = await fixture( - html`` + html`` ); const svg = el.shadowRoot?.querySelector('svg'); expect(svg?.getAttribute('aria-label')).toBe('Exposed height: Over 1000 meters'); From 0f76f0e41d0d489869ddd086894d40234559cfea Mon Sep 17 00:00:00 2001 From: gruble Date: Fri, 12 Jun 2026 16:42:44 +0200 Subject: [PATCH 6/6] Fikset etter kommentarer i PR --- .../nve-exposed-height.component.ts | 133 ++++++++++-------- 1 file changed, 71 insertions(+), 62 deletions(-) diff --git a/src/components/nve-exposed-height/nve-exposed-height.component.ts b/src/components/nve-exposed-height/nve-exposed-height.component.ts index e31f7d45..3737112e 100644 --- a/src/components/nve-exposed-height/nve-exposed-height.component.ts +++ b/src/components/nve-exposed-height/nve-exposed-height.component.ts @@ -3,6 +3,7 @@ import { customElement, property } from 'lit/decorators.js'; import { INveComponent } from '@interfaces/NveComponent.interface'; import styles from './nve-exposed-height.styles'; +/** Modell for tegning av fjellet */ interface MountainPaths { top: string; bottom: string; @@ -41,25 +42,34 @@ export default class NveExposedHeight extends LitElement implements INveComponen return this.lang.toLowerCase().startsWith('en'); } - // grafikk for fjellet - private createMountainPath(variant: 1 | 2 | 3 | 4): MountainPaths { - // Variant 1 og 2 bruker samme geometri - const twoPartMountain = { - top: 'M34.1299,22.8691 L20.1809,1.0741 C19.7079,0.3711 19.1749,0.0461 18.5749,0.0961 C17.9749,0.1461 17.4999,0.5581 17.1459,1.3301 L8.5139,18.9511 C10.8769,18.8161 12.7759,20.6831 12.7759,20.6831 C13.8629,21.6761 14.2179,22.0151 16.1509,22.0291 C19.1399,22.0511 20.0179,20.7691 22.0269,21.4881 C24.3919,22.3371 25.3159,23.3921 27.1709,24.5401 C29.9429,26.2481 31.3449,26.2291 33.0259,25.1611 C33.0259,25.1611 34.6099,24.2071 34.1299,22.8691', - bottom: - 'M6.4121,23.752 L1.0791,34.822 C0.6321,35.565 0.5661,36.549 0.8771,37.084 C1.1851,37.623 1.7671,38.58 2.6191,38.58 L39.6821,38.58 C40.5341,38.58 41.1151,37.623 41.4251,37.084 C41.7341,36.549 41.6681,35.219 41.2231,34.479 L36.2291,26.217 C36.4201,27.445 34.3141,28.854 34.3141,28.854 C32.6091,29.992 30.3481,29.959 27.8371,28.975 C25.7441,28.152 24.4491,26.895 22.4571,25.955 C20.4671,25.012 19.3401,26.041 16.5901,26.281 C14.9531,26.424 13.9711,26.166 12.6221,25.324 C12.6221,25.324 9.9561,23.752 6.4601,23.752 L6.4121,23.752 Z', - }; - - // Variant 3 og 4 bruker samme geometri - const threePartMountain = { - top: 'M23.917,10.0605 L19.813,2.6985 C19.387,2.0665 18.906,1.7735 18.367,1.8185 C17.826,1.8645 17.4,2.2345 17.08,2.9285 L14.471,8.8895 L14.384,9.0545 C14.384,9.0545 16.84,8.2885 18.863,9.4345 C20.521,10.3725 21.144,11.3805 23.917,10.0605', - middle: - 'M34.2402,27.625 C34.2402,28.483 33.0472,29.218 33.0472,29.218 C31.3652,30.286 29.3092,30.762 26.6252,28.92 C24.6822,27.587 23.7362,26.562 21.4802,25.868 C19.4402,25.241 18.5942,26.431 15.6052,26.409 C13.6722,26.396 13.2832,25.827 12.2282,25.063 C12.2282,25.063 10.6742,23.747 8.6982,23.268 L8.0602,23.169 L12.9712,12.208 C12.9712,12.208 16.3302,11.472 18.5802,12.745 C20.4222,13.788 23.3102,15.342 25.7672,13.209 L34.2402,27.625', - bottom: - 'M5.9922,27.7842 L0.8322,39.2022 C0.3852,39.9442 0.3202,40.9292 0.6312,41.4642 C0.9392,42.0032 1.5212,42.9602 2.3732,42.9602 L39.4352,42.9602 C40.2872,42.9602 40.8692,42.0032 41.1782,41.4642 C41.4882,40.9292 41.4222,39.5982 40.9762,38.8582 L36.0102,30.6382 C35.9552,31.7022 34.0682,33.2332 34.0682,33.2332 C32.3632,34.3722 30.1012,34.3392 27.5902,33.3542 C25.4982,32.5322 24.2032,31.2742 22.2112,30.3352 C20.2212,29.3912 19.0942,30.4212 16.3442,30.6612 C14.7072,30.8042 13.7242,30.5462 12.3752,29.7042 C12.3752,29.7042 9.9982,27.7842 6.5022,27.7842 L5.9922,27.7842 Z', - }; + /** Tegne-instruksjoner for enten to- eller tre-delt fjell, avhengig av variant */ + private createMountainGeometry(variant: 1 | 2 | 3 | 4) { + if (variant === 1 || variant === 2) { + // To-delt fjell + return { + top: 'M34.1299,22.8691 L20.1809,1.0741 C19.7079,0.3711 19.1749,0.0461 18.5749,0.0961 C17.9749,0.1461 17.4999,0.5581 17.1459,1.3301 L8.5139,18.9511 C10.8769,18.8161 12.7759,20.6831 12.7759,20.6831 C13.8629,21.6761 14.2179,22.0151 16.1509,22.0291 C19.1399,22.0511 20.0179,20.7691 22.0269,21.4881 C24.3919,22.3371 25.3159,23.3921 27.1709,24.5401 C29.9429,26.2481 31.3449,26.2291 33.0259,25.1611 C33.0259,25.1611 34.6099,24.2071 34.1299,22.8691', + bottom: + 'M6.4121,23.752 L1.0791,34.822 C0.6321,35.565 0.5661,36.549 0.8771,37.084 C1.1851,37.623 1.7671,38.58 2.6191,38.58 L39.6821,38.58 C40.5341,38.58 41.1151,37.623 41.4251,37.084 C41.7341,36.549 41.6681,35.219 41.2231,34.479 L36.2291,26.217 C36.4201,27.445 34.3141,28.854 34.3141,28.854 C32.6091,29.992 30.3481,29.959 27.8371,28.975 C25.7441,28.152 24.4491,26.895 22.4571,25.955 C20.4671,25.012 19.3401,26.041 16.5901,26.281 C14.9531,26.424 13.9711,26.166 12.6221,25.324 C12.6221,25.324 9.9561,23.752 6.4601,23.752 L6.4121,23.752 Z', + }; + } else { + // Tre-delt fjell + return { + top: 'M23.917,10.0605 L19.813,2.6985 C19.387,2.0665 18.906,1.7735 18.367,1.8185 C17.826,1.8645 17.4,2.2345 17.08,2.9285 L14.471,8.8895 L14.384,9.0545 C14.384,9.0545 16.84,8.2885 18.863,9.4345 C20.521,10.3725 21.144,11.3805 23.917,10.0605', + middle: + 'M34.2402,27.625 C34.2402,28.483 33.0472,29.218 33.0472,29.218 C31.3652,30.286 29.3092,30.762 26.6252,28.92 C24.6822,27.587 23.7362,26.562 21.4802,25.868 C19.4402,25.241 18.5942,26.431 15.6052,26.409 C13.6722,26.396 13.2832,25.827 12.2282,25.063 C12.2282,25.063 10.6742,23.747 8.6982,23.268 L8.0602,23.169 L12.9712,12.208 C12.9712,12.208 16.3302,11.472 18.5802,12.745 C20.4222,13.788 23.3102,15.342 25.7672,13.209 L34.2402,27.625', + bottom: + 'M5.9922,27.7842 L0.8322,39.2022 C0.3852,39.9442 0.3202,40.9292 0.6312,41.4642 C0.9392,42.0032 1.5212,42.9602 2.3732,42.9602 L39.4352,42.9602 C40.2872,42.9602 40.8692,42.0032 41.1782,41.4642 C41.4882,40.9292 41.4222,39.5982 40.9762,38.8582 L36.0102,30.6382 C35.9552,31.7022 34.0682,33.2332 34.0682,33.2332 C32.3632,34.3722 30.1012,34.3392 27.5902,33.3542 C25.4982,32.5322 24.2032,31.2742 22.2112,30.3352 C20.2212,29.3912 19.0942,30.4212 16.3442,30.6612 C14.7072,30.8042 13.7242,30.5462 12.3752,29.7042 C12.3752,29.7042 9.9982,27.7842 6.5022,27.7842 L5.9922,27.7842 Z', + }; + } + } - // Bare fargeklassene endres mellom variantene + /** + * Lager geometri og fargeklasser for tegning av fjellet + * @param variant hver variant har forskjellig geometri og fargelegging + * @returns Et objekt som inneholder SVG-paths og tilhørende fargeklasser for aktuell variant + */ + private createMountainPath(variant: 1 | 2 | 3 | 4): MountainPaths { + // Fargeklasser for hver variant const colorMap = { 1: { topClass: 'mountain-danger', bottomClass: 'mountain-safe' }, 2: { topClass: 'mountain-safe', bottomClass: 'mountain-danger' }, @@ -75,39 +85,17 @@ export default class NveExposedHeight extends LitElement implements INveComponen }, }; - const geometry = variant <= 2 ? twoPartMountain : threePartMountain; + const geometry = this.createMountainGeometry(variant); return { ...geometry, ...colorMap[variant] }; } + /** Lager geometri for pil */ private createArrowPath() { return 'M11.92,8 L7.96,8 L7.96,16 L3.98,16 L3.98,8 L0,8 L5.96,0 Z'; } - render() { - const paths = this.createMountainPath(this.variant); - // Fill 3 og 4 trenger en y-offset på -6 for å vises riktig - const mountainYOffset = this.variant === 3 || this.variant === 4 ? -6 : 0; - - const isThreePart = this.variant === 3 || this.variant === 4; - const vbY = isThreePart ? -6 : -2; - const vbH = isThreePart ? 48 : 46; - - return html` - - ${this.renderMountain(paths)} - ${this.renderArrowsAndLabels()} - - `; - } - + /** Lager SVG-elementer for fjellet */ private renderMountain(paths: MountainPaths) { if (this.variant === 3 || this.variant === 4) { return svg` @@ -123,21 +111,23 @@ export default class NveExposedHeight extends LitElement implements INveComponen } } + /** Lager SVG-elementer for piler og etiketter */ private renderArrowsAndLabels() { const mountainYOffset = this.variant === 3 || this.variant === 4 ? -6 : 0; const mountainHeight = this.variant === 3 || this.variant === 4 ? 47 : 39; const centerY = mountainHeight / 2 + mountainYOffset; + // Posisjon og rotasjon for pilene, samt tekst og posisjon for etikettene. Hver variant har sin egen konfigurasjon. const config = { 1: { groupHeight: 28, arrows: [{ y: 0, rotation: 0 }], - texts: [{ y: 30, text: `${this.height1}m`, xOffset: 5.96, centered: true }], // Sentrert på pil + texts: [{ y: 30, text: `${this.height1}m`, xOffset: 5.96, centered: true }], }, 2: { groupHeight: 28, arrows: [{ y: 12, rotation: 180 }], - texts: [{ y: 8, text: `${this.height1}m`, xOffset: 5.96, centered: true }], // Sentrert på pil + texts: [{ y: 8, text: `${this.height1}m`, xOffset: 5.96, centered: true }], }, 3: { groupHeight: 38, @@ -146,8 +136,8 @@ export default class NveExposedHeight extends LitElement implements INveComponen { y: 22, rotation: 180 }, ], texts: [ - { y: 16, text: `${this.maxHeight}m`, xOffset: 16, centered: false }, // was y=20 → now baseline = shaft bottom of up-arrow - { y: 30, text: `${this.minHeight}m`, xOffset: 16, centered: false }, // was y=38 + { y: 16, text: `${this.maxHeight}m`, xOffset: 16, centered: false }, + { y: 30, text: `${this.minHeight}m`, xOffset: 16, centered: false }, ], }, 4: { @@ -170,30 +160,24 @@ export default class NveExposedHeight extends LitElement implements INveComponen `; } + /** Lager SVG-element for pil. Hvis rotation er angitt, snus pila på hodet */ private renderArrow(y: number, rotation: number) { - const arrowPath = this.createArrowPath(); - - if (rotation === 0) { - return svg` - - - - `; - } else { - return svg` - - - - `; - } + const rotate = rotation !== 0 ? ' rotate(180 5.96 8)' : ''; + return svg` + + + + `; } + /** Lager SVG-element for tekst */ private renderText(y: number, text: string, xOffset: number, centered: boolean) { const x = xOffset; const className = centered ? 'height-label centered' : 'height-label'; return svg`${text}`; } + /** Lager ARIA-label for SVG-elementet */ private getAriaLabel(): string { const prefix = this.isEnglish() ? 'Exposed height' : 'Utsatt høyde'; const between = this.isEnglish() ? 'Between' : 'Mellom'; @@ -223,6 +207,31 @@ export default class NveExposedHeight extends LitElement implements INveComponen private get minHeight(): number { return Math.min(this.height1, this.height2); } + + /** Tegn komponenten */ + render() { + const paths = this.createMountainPath(this.variant); + // Fill 3 og 4 trenger en y-offset på -6 for å vises riktig + const mountainYOffset = this.variant === 3 || this.variant === 4 ? -6 : 0; + + const isThreePart = this.variant === 3 || this.variant === 4; + const vbY = isThreePart ? -6 : -2; + const vbH = isThreePart ? 48 : 46; + + return html` + + ${this.renderMountain(paths)} + ${this.renderArrowsAndLabels()} + + `; + } } declare global {