@helsevestikt/hviktor-angular 0.0.15 → 0.0.16
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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Input, Component, booleanAttribute, Directive, CUSTOM_ELEMENTS_SCHEMA, EventEmitter, inject, ElementRef, HostListener, Output, ViewChild, DestroyRef, signal, input, computed, HostBinding, ChangeDetectorRef, ViewEncapsulation, ContentChildren, forwardRef, Renderer2 } from '@angular/core';
|
|
2
|
+
import { Input, Component, booleanAttribute, Directive, CUSTOM_ELEMENTS_SCHEMA, EventEmitter, inject, ElementRef, HostListener, Output, ViewChild, DestroyRef, signal, input, computed, HostBinding, ChangeDetectorRef, ViewEncapsulation, numberAttribute, ContentChildren, forwardRef, Renderer2 } from '@angular/core';
|
|
3
3
|
import '@u-elements/u-details';
|
|
4
4
|
import { FormGroupDirective, NgControl, ControlContainer, Validators, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
5
5
|
import { HttpClient } from '@angular/common/http';
|
|
@@ -2021,6 +2021,273 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
2021
2021
|
}]
|
|
2022
2022
|
}] });
|
|
2023
2023
|
|
|
2024
|
+
/**
|
|
2025
|
+
* @summary
|
|
2026
|
+
* Pagination er en liste med knapper som brukes for å navigere mellom
|
|
2027
|
+
* ulike sider med innhold, for eksempel søkeresultater eller tabeller.
|
|
2028
|
+
*
|
|
2029
|
+
* @example
|
|
2030
|
+
* ```html
|
|
2031
|
+
* <hvi-pagination
|
|
2032
|
+
* [totalItems]="100"
|
|
2033
|
+
* [pageSize]="10"
|
|
2034
|
+
* [(currentPage)]="currentPage"
|
|
2035
|
+
* />
|
|
2036
|
+
* ```
|
|
2037
|
+
*
|
|
2038
|
+
* @see {@link https://designsystemet.no/no/components/docs/pagination/code}
|
|
2039
|
+
*/
|
|
2040
|
+
class HviPagination {
|
|
2041
|
+
/** Totalt antall elementer som pagineres */
|
|
2042
|
+
set totalItems(value) {
|
|
2043
|
+
this._totalItems.set(value);
|
|
2044
|
+
}
|
|
2045
|
+
/** Antall elementer per side */
|
|
2046
|
+
set pageSize(value) {
|
|
2047
|
+
this._pageSize.set(value);
|
|
2048
|
+
}
|
|
2049
|
+
/** Nåværende side (1-indeksert) */
|
|
2050
|
+
set currentPage(value) {
|
|
2051
|
+
this._currentPage.set(value);
|
|
2052
|
+
}
|
|
2053
|
+
/** Antall sider som skal vises rundt nåværende side */
|
|
2054
|
+
siblingCount = 1;
|
|
2055
|
+
/** Vis alltid første og siste side */
|
|
2056
|
+
showEdges = true;
|
|
2057
|
+
/** Vis "Forrige" og "Neste" tekst på knappene */
|
|
2058
|
+
showPreviousNextLabels = true;
|
|
2059
|
+
/** Aria-label for hele navigasjonen */
|
|
2060
|
+
ariaLabel = 'Sidenavigering';
|
|
2061
|
+
/** Tekst for forrige-knappen */
|
|
2062
|
+
previousLabel = 'Forrige';
|
|
2063
|
+
/** Tekst for neste-knappen */
|
|
2064
|
+
nextLabel = 'Neste';
|
|
2065
|
+
/** Event som emitteres når siden endres */
|
|
2066
|
+
currentPageChange = new EventEmitter();
|
|
2067
|
+
/** Event som emitteres med mer detaljer om sideendring */
|
|
2068
|
+
pageChange = new EventEmitter();
|
|
2069
|
+
// Internal signals
|
|
2070
|
+
_totalItems = signal(0, ...(ngDevMode ? [{ debugName: "_totalItems" }] : []));
|
|
2071
|
+
_pageSize = signal(10, ...(ngDevMode ? [{ debugName: "_pageSize" }] : []));
|
|
2072
|
+
_currentPage = signal(1, ...(ngDevMode ? [{ debugName: "_currentPage" }] : []));
|
|
2073
|
+
/** Beregnet totalt antall sider */
|
|
2074
|
+
totalPages = computed(() => {
|
|
2075
|
+
return Math.max(1, Math.ceil(this._totalItems() / this._pageSize()));
|
|
2076
|
+
}, ...(ngDevMode ? [{ debugName: "totalPages" }] : []));
|
|
2077
|
+
/** Er vi på første side? */
|
|
2078
|
+
isFirstPage = computed(() => this._currentPage() <= 1, ...(ngDevMode ? [{ debugName: "isFirstPage" }] : []));
|
|
2079
|
+
/** Er vi på siste side? */
|
|
2080
|
+
isLastPage = computed(() => this._currentPage() >= this.totalPages(), ...(ngDevMode ? [{ debugName: "isLastPage" }] : []));
|
|
2081
|
+
/** Beregner hvilke elementer som skal vises i pagineringen */
|
|
2082
|
+
paginationItems = computed(() => {
|
|
2083
|
+
const current = this._currentPage();
|
|
2084
|
+
const total = this.totalPages();
|
|
2085
|
+
const siblings = this.siblingCount;
|
|
2086
|
+
if (total <= 1)
|
|
2087
|
+
return [];
|
|
2088
|
+
const items = [];
|
|
2089
|
+
// Beregn range rundt nåværende side
|
|
2090
|
+
const leftSibling = Math.max(current - siblings, 1);
|
|
2091
|
+
const rightSibling = Math.min(current + siblings, total);
|
|
2092
|
+
const showLeftEllipsis = this.showEdges && leftSibling > 2;
|
|
2093
|
+
const showRightEllipsis = this.showEdges && rightSibling < total - 1;
|
|
2094
|
+
// Første side (hvis showEdges)
|
|
2095
|
+
if (this.showEdges && leftSibling > 1) {
|
|
2096
|
+
items.push({ type: 'page', page: 1 });
|
|
2097
|
+
if (showLeftEllipsis) {
|
|
2098
|
+
items.push({ type: 'ellipsis' });
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
// Sider rundt nåværende
|
|
2102
|
+
for (let page = leftSibling; page <= rightSibling; page++) {
|
|
2103
|
+
items.push({ type: 'page', page });
|
|
2104
|
+
}
|
|
2105
|
+
// Siste side (hvis showEdges)
|
|
2106
|
+
if (this.showEdges && rightSibling < total) {
|
|
2107
|
+
if (showRightEllipsis) {
|
|
2108
|
+
items.push({ type: 'ellipsis' });
|
|
2109
|
+
}
|
|
2110
|
+
items.push({ type: 'page', page: total });
|
|
2111
|
+
}
|
|
2112
|
+
return items;
|
|
2113
|
+
}, ...(ngDevMode ? [{ debugName: "paginationItems" }] : []));
|
|
2114
|
+
/** Gå til en spesifikk side */
|
|
2115
|
+
goToPage(page) {
|
|
2116
|
+
const previousPage = this._currentPage();
|
|
2117
|
+
const newPage = Math.max(1, Math.min(page, this.totalPages()));
|
|
2118
|
+
if (newPage !== previousPage) {
|
|
2119
|
+
this._currentPage.set(newPage);
|
|
2120
|
+
this.currentPageChange.emit(newPage);
|
|
2121
|
+
this.pageChange.emit({ page: newPage, previousPage });
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
/** Gå til forrige side */
|
|
2125
|
+
goToPrevious() {
|
|
2126
|
+
if (!this.isFirstPage()) {
|
|
2127
|
+
this.goToPage(this._currentPage() - 1);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
/** Gå til neste side */
|
|
2131
|
+
goToNext() {
|
|
2132
|
+
if (!this.isLastPage()) {
|
|
2133
|
+
this.goToPage(this._currentPage() + 1);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
/** Gå til første side */
|
|
2137
|
+
goToFirst() {
|
|
2138
|
+
this.goToPage(1);
|
|
2139
|
+
}
|
|
2140
|
+
/** Gå til siste side */
|
|
2141
|
+
goToLast() {
|
|
2142
|
+
this.goToPage(this.totalPages());
|
|
2143
|
+
}
|
|
2144
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviPagination, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2145
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.2", type: HviPagination, isStandalone: true, selector: "hvi-pagination", inputs: { totalItems: ["totalItems", "totalItems", numberAttribute], pageSize: ["pageSize", "pageSize", numberAttribute], currentPage: ["currentPage", "currentPage", numberAttribute], siblingCount: ["siblingCount", "siblingCount", numberAttribute], showEdges: ["showEdges", "showEdges", booleanAttribute], showPreviousNextLabels: ["showPreviousNextLabels", "showPreviousNextLabels", booleanAttribute], ariaLabel: "ariaLabel", previousLabel: "previousLabel", nextLabel: "nextLabel" }, outputs: { currentPageChange: "currentPageChange", pageChange: "pageChange" }, host: { classAttribute: "hvi-pagination" }, ngImport: i0, template: `
|
|
2146
|
+
<nav [attr.aria-label]="ariaLabel" class="ds-pagination">
|
|
2147
|
+
<ul>
|
|
2148
|
+
<!-- Forrige -->
|
|
2149
|
+
<li>
|
|
2150
|
+
<button
|
|
2151
|
+
class="ds-button"
|
|
2152
|
+
data-variant="tertiary"
|
|
2153
|
+
type="button"
|
|
2154
|
+
[attr.aria-label]="previousLabel"
|
|
2155
|
+
[disabled]="isFirstPage()"
|
|
2156
|
+
(click)="goToPrevious()"
|
|
2157
|
+
>
|
|
2158
|
+
{{ showPreviousNextLabels ? previousLabel : '' }}
|
|
2159
|
+
</button>
|
|
2160
|
+
</li>
|
|
2161
|
+
|
|
2162
|
+
<!-- Sidetall -->
|
|
2163
|
+
@for (item of paginationItems(); track $index) {
|
|
2164
|
+
@if (item.type === 'page') {
|
|
2165
|
+
<li>
|
|
2166
|
+
<button
|
|
2167
|
+
class="ds-button"
|
|
2168
|
+
[attr.data-variant]="item.page === _currentPage() ? 'primary' : 'tertiary'"
|
|
2169
|
+
type="button"
|
|
2170
|
+
[attr.aria-label]="'Side ' + item.page"
|
|
2171
|
+
[attr.aria-current]="item.page === _currentPage() ? 'page' : null"
|
|
2172
|
+
(click)="goToPage(item.page)"
|
|
2173
|
+
>
|
|
2174
|
+
{{ item.page }}
|
|
2175
|
+
</button>
|
|
2176
|
+
</li>
|
|
2177
|
+
} @else if (item.type === 'ellipsis') {
|
|
2178
|
+
<li></li>
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
<!-- Neste -->
|
|
2183
|
+
<li>
|
|
2184
|
+
<button
|
|
2185
|
+
class="ds-button"
|
|
2186
|
+
data-variant="tertiary"
|
|
2187
|
+
type="button"
|
|
2188
|
+
[attr.aria-label]="nextLabel"
|
|
2189
|
+
[disabled]="isLastPage()"
|
|
2190
|
+
(click)="goToNext()"
|
|
2191
|
+
>
|
|
2192
|
+
{{ showPreviousNextLabels ? nextLabel : '' }}
|
|
2193
|
+
</button>
|
|
2194
|
+
</li>
|
|
2195
|
+
</ul>
|
|
2196
|
+
</nav>
|
|
2197
|
+
`, isInline: true });
|
|
2198
|
+
}
|
|
2199
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviPagination, decorators: [{
|
|
2200
|
+
type: Component,
|
|
2201
|
+
args: [{
|
|
2202
|
+
selector: 'hvi-pagination',
|
|
2203
|
+
standalone: true,
|
|
2204
|
+
template: `
|
|
2205
|
+
<nav [attr.aria-label]="ariaLabel" class="ds-pagination">
|
|
2206
|
+
<ul>
|
|
2207
|
+
<!-- Forrige -->
|
|
2208
|
+
<li>
|
|
2209
|
+
<button
|
|
2210
|
+
class="ds-button"
|
|
2211
|
+
data-variant="tertiary"
|
|
2212
|
+
type="button"
|
|
2213
|
+
[attr.aria-label]="previousLabel"
|
|
2214
|
+
[disabled]="isFirstPage()"
|
|
2215
|
+
(click)="goToPrevious()"
|
|
2216
|
+
>
|
|
2217
|
+
{{ showPreviousNextLabels ? previousLabel : '' }}
|
|
2218
|
+
</button>
|
|
2219
|
+
</li>
|
|
2220
|
+
|
|
2221
|
+
<!-- Sidetall -->
|
|
2222
|
+
@for (item of paginationItems(); track $index) {
|
|
2223
|
+
@if (item.type === 'page') {
|
|
2224
|
+
<li>
|
|
2225
|
+
<button
|
|
2226
|
+
class="ds-button"
|
|
2227
|
+
[attr.data-variant]="item.page === _currentPage() ? 'primary' : 'tertiary'"
|
|
2228
|
+
type="button"
|
|
2229
|
+
[attr.aria-label]="'Side ' + item.page"
|
|
2230
|
+
[attr.aria-current]="item.page === _currentPage() ? 'page' : null"
|
|
2231
|
+
(click)="goToPage(item.page)"
|
|
2232
|
+
>
|
|
2233
|
+
{{ item.page }}
|
|
2234
|
+
</button>
|
|
2235
|
+
</li>
|
|
2236
|
+
} @else if (item.type === 'ellipsis') {
|
|
2237
|
+
<li></li>
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
<!-- Neste -->
|
|
2242
|
+
<li>
|
|
2243
|
+
<button
|
|
2244
|
+
class="ds-button"
|
|
2245
|
+
data-variant="tertiary"
|
|
2246
|
+
type="button"
|
|
2247
|
+
[attr.aria-label]="nextLabel"
|
|
2248
|
+
[disabled]="isLastPage()"
|
|
2249
|
+
(click)="goToNext()"
|
|
2250
|
+
>
|
|
2251
|
+
{{ showPreviousNextLabels ? nextLabel : '' }}
|
|
2252
|
+
</button>
|
|
2253
|
+
</li>
|
|
2254
|
+
</ul>
|
|
2255
|
+
</nav>
|
|
2256
|
+
`,
|
|
2257
|
+
host: {
|
|
2258
|
+
class: 'hvi-pagination',
|
|
2259
|
+
},
|
|
2260
|
+
}]
|
|
2261
|
+
}], propDecorators: { totalItems: [{
|
|
2262
|
+
type: Input,
|
|
2263
|
+
args: [{ transform: numberAttribute, required: true }]
|
|
2264
|
+
}], pageSize: [{
|
|
2265
|
+
type: Input,
|
|
2266
|
+
args: [{ transform: numberAttribute }]
|
|
2267
|
+
}], currentPage: [{
|
|
2268
|
+
type: Input,
|
|
2269
|
+
args: [{ transform: numberAttribute }]
|
|
2270
|
+
}], siblingCount: [{
|
|
2271
|
+
type: Input,
|
|
2272
|
+
args: [{ transform: numberAttribute }]
|
|
2273
|
+
}], showEdges: [{
|
|
2274
|
+
type: Input,
|
|
2275
|
+
args: [{ transform: booleanAttribute }]
|
|
2276
|
+
}], showPreviousNextLabels: [{
|
|
2277
|
+
type: Input,
|
|
2278
|
+
args: [{ transform: booleanAttribute }]
|
|
2279
|
+
}], ariaLabel: [{
|
|
2280
|
+
type: Input
|
|
2281
|
+
}], previousLabel: [{
|
|
2282
|
+
type: Input
|
|
2283
|
+
}], nextLabel: [{
|
|
2284
|
+
type: Input
|
|
2285
|
+
}], currentPageChange: [{
|
|
2286
|
+
type: Output
|
|
2287
|
+
}], pageChange: [{
|
|
2288
|
+
type: Output
|
|
2289
|
+
}] } });
|
|
2290
|
+
|
|
2024
2291
|
/**
|
|
2025
2292
|
* Paragraph is used for continuous text and is typically applied in articles, components, help text, and similar content.
|
|
2026
2293
|
*
|
|
@@ -2608,6 +2875,442 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
2608
2875
|
type: Input
|
|
2609
2876
|
}] } });
|
|
2610
2877
|
|
|
2878
|
+
/**
|
|
2879
|
+
* @summary
|
|
2880
|
+
* Table brukes for å vise strukturert informasjon på en ryddig og oversiktlig måte.
|
|
2881
|
+
* Tabeller kan gjøre det enklere for brukerne å skanne og sammenligne informasjon.
|
|
2882
|
+
*
|
|
2883
|
+
* @example
|
|
2884
|
+
* ```html
|
|
2885
|
+
* <!-- Enkel bruk med innebygd sortering og søk -->
|
|
2886
|
+
* <table hviTable [value]="persons" [globalFilterFields]="['navn', 'epost']" #table="hviTable">
|
|
2887
|
+
* <caption>
|
|
2888
|
+
* <input type="search" (input)="table.filterGlobal($event.target.value)" />
|
|
2889
|
+
* </caption>
|
|
2890
|
+
* <thead>
|
|
2891
|
+
* <tr>
|
|
2892
|
+
* <th hviSortableColumn="navn">
|
|
2893
|
+
* <button>Navn</button>
|
|
2894
|
+
* </th>
|
|
2895
|
+
* <th>Epost</th>
|
|
2896
|
+
* </tr>
|
|
2897
|
+
* </thead>
|
|
2898
|
+
* <tbody>
|
|
2899
|
+
* @for (person of table.filteredValue(); track person.id) {
|
|
2900
|
+
* <tr>
|
|
2901
|
+
* <td>{{ person.navn }}</td>
|
|
2902
|
+
* <td>{{ person.epost }}</td>
|
|
2903
|
+
* </tr>
|
|
2904
|
+
* }
|
|
2905
|
+
* </tbody>
|
|
2906
|
+
* </table>
|
|
2907
|
+
* ```
|
|
2908
|
+
*
|
|
2909
|
+
* @see {@link https://designsystemet.no/no/components/docs/table/code}
|
|
2910
|
+
*/
|
|
2911
|
+
class HviTable {
|
|
2912
|
+
/** Gir tabellen zebrastriper (annenhver rad har alternativ bakgrunn) */
|
|
2913
|
+
zebra = false;
|
|
2914
|
+
/** Gir tabellen en avrundet kant rundt */
|
|
2915
|
+
border = false;
|
|
2916
|
+
/** Gir tabellen hover-effekt på rader */
|
|
2917
|
+
hover = false;
|
|
2918
|
+
/** Gjør tabellens header sticky (fester seg til toppen ved scrolling) */
|
|
2919
|
+
stickyHeader = false;
|
|
2920
|
+
/** Data som skal vises i tabellen */
|
|
2921
|
+
set value(data) {
|
|
2922
|
+
this._value.set(data ?? []);
|
|
2923
|
+
}
|
|
2924
|
+
/** Felt som data skal sorteres etter (valgfri initial verdi) */
|
|
2925
|
+
set sortField(field) {
|
|
2926
|
+
if (field) {
|
|
2927
|
+
this._sortField.set(field);
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
/** Sorteringsretning: 1 = ascending, -1 = descending, 0 = none */
|
|
2931
|
+
set sortOrder(order) {
|
|
2932
|
+
this._sortOrder.set(order);
|
|
2933
|
+
}
|
|
2934
|
+
/** Felt som global søk skal søke i */
|
|
2935
|
+
globalFilterFields = [];
|
|
2936
|
+
/** Aktiver paginering */
|
|
2937
|
+
paginator = false;
|
|
2938
|
+
/** Antall rader per side (når paginator er aktivert) */
|
|
2939
|
+
set rows(value) {
|
|
2940
|
+
this._rows.set(value);
|
|
2941
|
+
}
|
|
2942
|
+
/** Indeks for første rad som vises (0-basert) */
|
|
2943
|
+
set first(value) {
|
|
2944
|
+
this._first.set(value);
|
|
2945
|
+
}
|
|
2946
|
+
/** Event som emitteres når sortering endres */
|
|
2947
|
+
sortChange = new EventEmitter();
|
|
2948
|
+
/** Event som emitteres når side endres */
|
|
2949
|
+
pageChange = new EventEmitter();
|
|
2950
|
+
/** Event som emitteres med nåværende side (1-basert, for two-way binding) */
|
|
2951
|
+
currentPageChange = new EventEmitter();
|
|
2952
|
+
// Internal signals
|
|
2953
|
+
_value = signal([], ...(ngDevMode ? [{ debugName: "_value" }] : []));
|
|
2954
|
+
_sortField = signal(null, ...(ngDevMode ? [{ debugName: "_sortField" }] : []));
|
|
2955
|
+
_sortOrder = signal(0, ...(ngDevMode ? [{ debugName: "_sortOrder" }] : [])); // 0 = none, 1 = asc, -1 = desc
|
|
2956
|
+
_globalFilter = signal(null, ...(ngDevMode ? [{ debugName: "_globalFilter" }] : []));
|
|
2957
|
+
_rows = signal(10, ...(ngDevMode ? [{ debugName: "_rows" }] : []));
|
|
2958
|
+
_first = signal(0, ...(ngDevMode ? [{ debugName: "_first" }] : []));
|
|
2959
|
+
/** Kun sortert data (uten søk) - for bakoverkompatibilitet */
|
|
2960
|
+
sortedValue = computed(() => {
|
|
2961
|
+
return this.applySorting(this._value());
|
|
2962
|
+
}, ...(ngDevMode ? [{ debugName: "sortedValue" }] : []));
|
|
2963
|
+
/** Filtrert og sortert data - bruk denne i template */
|
|
2964
|
+
filteredValue = computed(() => {
|
|
2965
|
+
const data = this._value();
|
|
2966
|
+
const filtered = this.applyGlobalFilter(data);
|
|
2967
|
+
return this.applySorting(filtered);
|
|
2968
|
+
}, ...(ngDevMode ? [{ debugName: "filteredValue" }] : []));
|
|
2969
|
+
/** Paginert, filtrert og sortert data - bruk denne når paginator er aktivert */
|
|
2970
|
+
paginatedValue = computed(() => {
|
|
2971
|
+
const data = this.filteredValue();
|
|
2972
|
+
if (!this.paginator)
|
|
2973
|
+
return data;
|
|
2974
|
+
const first = this._first();
|
|
2975
|
+
const rows = this._rows();
|
|
2976
|
+
return data.slice(first, first + rows);
|
|
2977
|
+
}, ...(ngDevMode ? [{ debugName: "paginatedValue" }] : []));
|
|
2978
|
+
/** Antall rader etter søk */
|
|
2979
|
+
totalFilteredRecords = computed(() => this.filteredValue().length, ...(ngDevMode ? [{ debugName: "totalFilteredRecords" }] : []));
|
|
2980
|
+
/** Antall rader totalt (før søk) */
|
|
2981
|
+
totalRecords = computed(() => this._value().length, ...(ngDevMode ? [{ debugName: "totalRecords" }] : []));
|
|
2982
|
+
/** Totalt antall sider */
|
|
2983
|
+
pageCount = computed(() => {
|
|
2984
|
+
if (!this.paginator)
|
|
2985
|
+
return 1;
|
|
2986
|
+
return Math.max(1, Math.ceil(this.totalFilteredRecords() / this._rows()));
|
|
2987
|
+
}, ...(ngDevMode ? [{ debugName: "pageCount" }] : []));
|
|
2988
|
+
/** Nåværende side (1-basert) */
|
|
2989
|
+
currentPage = computed(() => {
|
|
2990
|
+
return Math.floor(this._first() / this._rows()) + 1;
|
|
2991
|
+
}, ...(ngDevMode ? [{ debugName: "currentPage" }] : []));
|
|
2992
|
+
/** Er vi på første side? */
|
|
2993
|
+
isFirstPage = computed(() => this._first() === 0, ...(ngDevMode ? [{ debugName: "isFirstPage" }] : []));
|
|
2994
|
+
/** Er vi på siste side? */
|
|
2995
|
+
isLastPage = computed(() => {
|
|
2996
|
+
return this._first() + this._rows() >= this.totalFilteredRecords();
|
|
2997
|
+
}, ...(ngDevMode ? [{ debugName: "isLastPage" }] : []));
|
|
2998
|
+
/** Nåværende sorteringsfelt */
|
|
2999
|
+
currentSortField = computed(() => this._sortField(), ...(ngDevMode ? [{ debugName: "currentSortField" }] : []));
|
|
3000
|
+
/** Nåværende sorteringsretning som SortDirection */
|
|
3001
|
+
currentSortDirection = computed(() => {
|
|
3002
|
+
const order = this._sortOrder();
|
|
3003
|
+
if (order === 1)
|
|
3004
|
+
return 'ascending';
|
|
3005
|
+
if (order === -1)
|
|
3006
|
+
return 'descending';
|
|
3007
|
+
return 'none';
|
|
3008
|
+
}, ...(ngDevMode ? [{ debugName: "currentSortDirection" }] : []));
|
|
3009
|
+
/** Nåværende søkeverdi */
|
|
3010
|
+
currentGlobalFilter = computed(() => this._globalFilter(), ...(ngDevMode ? [{ debugName: "currentGlobalFilter" }] : []));
|
|
3011
|
+
/**
|
|
3012
|
+
* Global søk - søker på tvers av alle felt i globalFilterFields.
|
|
3013
|
+
* @param value Søkeverdi
|
|
3014
|
+
*/
|
|
3015
|
+
filterGlobal(value) {
|
|
3016
|
+
this._globalFilter.set(value?.trim() || null);
|
|
3017
|
+
}
|
|
3018
|
+
/**
|
|
3019
|
+
* Nullstiller søk.
|
|
3020
|
+
*/
|
|
3021
|
+
clearFilter() {
|
|
3022
|
+
this._globalFilter.set(null);
|
|
3023
|
+
}
|
|
3024
|
+
/**
|
|
3025
|
+
* Nullstiller hele tabellen (sortering og søk).
|
|
3026
|
+
*/
|
|
3027
|
+
clear() {
|
|
3028
|
+
this._sortField.set(null);
|
|
3029
|
+
this._sortOrder.set(0);
|
|
3030
|
+
this._globalFilter.set(null);
|
|
3031
|
+
this._first.set(0);
|
|
3032
|
+
}
|
|
3033
|
+
// ========== Paginering ==========
|
|
3034
|
+
/**
|
|
3035
|
+
* Gå til en spesifikk side (1-basert).
|
|
3036
|
+
*/
|
|
3037
|
+
goToPage(page) {
|
|
3038
|
+
const totalPages = this.pageCount();
|
|
3039
|
+
const validPage = Math.max(1, Math.min(page, totalPages));
|
|
3040
|
+
const newFirst = (validPage - 1) * this._rows();
|
|
3041
|
+
if (newFirst !== this._first()) {
|
|
3042
|
+
this._first.set(newFirst);
|
|
3043
|
+
this.emitPageEvent();
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
/**
|
|
3047
|
+
* Gå til første side.
|
|
3048
|
+
*/
|
|
3049
|
+
goToFirstPage() {
|
|
3050
|
+
this.goToPage(1);
|
|
3051
|
+
}
|
|
3052
|
+
/**
|
|
3053
|
+
* Gå til siste side.
|
|
3054
|
+
*/
|
|
3055
|
+
goToLastPage() {
|
|
3056
|
+
this.goToPage(this.pageCount());
|
|
3057
|
+
}
|
|
3058
|
+
/**
|
|
3059
|
+
* Gå til forrige side.
|
|
3060
|
+
*/
|
|
3061
|
+
goToPreviousPage() {
|
|
3062
|
+
if (!this.isFirstPage()) {
|
|
3063
|
+
this.goToPage(this.currentPage() - 1);
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
/**
|
|
3067
|
+
* Gå til neste side.
|
|
3068
|
+
*/
|
|
3069
|
+
goToNextPage() {
|
|
3070
|
+
if (!this.isLastPage()) {
|
|
3071
|
+
this.goToPage(this.currentPage() + 1);
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
/**
|
|
3075
|
+
* Endre antall rader per side.
|
|
3076
|
+
*/
|
|
3077
|
+
setRows(rows) {
|
|
3078
|
+
this._rows.set(rows);
|
|
3079
|
+
this._first.set(0); // Reset til første side
|
|
3080
|
+
this.emitPageEvent();
|
|
3081
|
+
}
|
|
3082
|
+
emitPageEvent() {
|
|
3083
|
+
this.pageChange.emit({
|
|
3084
|
+
first: this._first(),
|
|
3085
|
+
rows: this._rows(),
|
|
3086
|
+
page: this.currentPage(),
|
|
3087
|
+
pageCount: this.pageCount(),
|
|
3088
|
+
});
|
|
3089
|
+
this.currentPageChange.emit(this.currentPage());
|
|
3090
|
+
}
|
|
3091
|
+
/**
|
|
3092
|
+
* Sorterer tabellen etter et felt.
|
|
3093
|
+
* Kalles av hviSortableColumn directive.
|
|
3094
|
+
*/
|
|
3095
|
+
sort(field) {
|
|
3096
|
+
const currentField = this._sortField();
|
|
3097
|
+
const currentOrder = this._sortOrder();
|
|
3098
|
+
let newOrder;
|
|
3099
|
+
if (currentField === field) {
|
|
3100
|
+
// Samme felt - sykl gjennom: asc → desc → none
|
|
3101
|
+
if (currentOrder === 1) {
|
|
3102
|
+
newOrder = -1;
|
|
3103
|
+
}
|
|
3104
|
+
else if (currentOrder === -1) {
|
|
3105
|
+
newOrder = 0;
|
|
3106
|
+
}
|
|
3107
|
+
else {
|
|
3108
|
+
newOrder = 1;
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
else {
|
|
3112
|
+
// Nytt felt - start med ascending
|
|
3113
|
+
newOrder = 1;
|
|
3114
|
+
}
|
|
3115
|
+
this._sortField.set(newOrder === 0 ? null : field);
|
|
3116
|
+
this._sortOrder.set(newOrder);
|
|
3117
|
+
const direction = newOrder === 1 ? 'ascending' : newOrder === -1 ? 'descending' : 'none';
|
|
3118
|
+
this.sortChange.emit({ field, direction });
|
|
3119
|
+
}
|
|
3120
|
+
/**
|
|
3121
|
+
* Henter sorteringsretning for et spesifikt felt.
|
|
3122
|
+
* Brukes av hviSortableColumn for å vise riktig aria-sort.
|
|
3123
|
+
*/
|
|
3124
|
+
getSortDirection(field) {
|
|
3125
|
+
if (this._sortField() !== field) {
|
|
3126
|
+
return 'none';
|
|
3127
|
+
}
|
|
3128
|
+
return this.currentSortDirection();
|
|
3129
|
+
}
|
|
3130
|
+
// ========== Private methods ==========
|
|
3131
|
+
applyGlobalFilter(data) {
|
|
3132
|
+
const globalFilter = this._globalFilter();
|
|
3133
|
+
const globalFields = this.globalFilterFields;
|
|
3134
|
+
if (!globalFilter || globalFields.length === 0 || data.length === 0) {
|
|
3135
|
+
return data;
|
|
3136
|
+
}
|
|
3137
|
+
const searchTerm = globalFilter.toLowerCase();
|
|
3138
|
+
return data.filter((item) => {
|
|
3139
|
+
return globalFields.some((field) => {
|
|
3140
|
+
const value = this.getFieldValue(item, field);
|
|
3141
|
+
return String(value ?? '')
|
|
3142
|
+
.toLowerCase()
|
|
3143
|
+
.includes(searchTerm);
|
|
3144
|
+
});
|
|
3145
|
+
});
|
|
3146
|
+
}
|
|
3147
|
+
applySorting(data) {
|
|
3148
|
+
const field = this._sortField();
|
|
3149
|
+
const order = this._sortOrder();
|
|
3150
|
+
if (!field || order === 0 || data.length === 0) {
|
|
3151
|
+
return data;
|
|
3152
|
+
}
|
|
3153
|
+
return [...data].sort((a, b) => {
|
|
3154
|
+
const valueA = this.getFieldValue(a, field);
|
|
3155
|
+
const valueB = this.getFieldValue(b, field);
|
|
3156
|
+
let comparison = 0;
|
|
3157
|
+
if (valueA == null && valueB == null) {
|
|
3158
|
+
comparison = 0;
|
|
3159
|
+
}
|
|
3160
|
+
else if (valueA == null) {
|
|
3161
|
+
comparison = -1;
|
|
3162
|
+
}
|
|
3163
|
+
else if (valueB == null) {
|
|
3164
|
+
comparison = 1;
|
|
3165
|
+
}
|
|
3166
|
+
else if (typeof valueA === 'string' && typeof valueB === 'string') {
|
|
3167
|
+
comparison = valueA.localeCompare(valueB, 'nb');
|
|
3168
|
+
}
|
|
3169
|
+
else if (typeof valueA === 'number' && typeof valueB === 'number') {
|
|
3170
|
+
comparison = valueA - valueB;
|
|
3171
|
+
}
|
|
3172
|
+
else {
|
|
3173
|
+
comparison = String(valueA).localeCompare(String(valueB), 'nb');
|
|
3174
|
+
}
|
|
3175
|
+
return order * comparison;
|
|
3176
|
+
});
|
|
3177
|
+
}
|
|
3178
|
+
/**
|
|
3179
|
+
* Henter verdi fra et objekt basert på felt-path (støtter nested: "user.name")
|
|
3180
|
+
*/
|
|
3181
|
+
getFieldValue(obj, field) {
|
|
3182
|
+
const keys = field.split('.');
|
|
3183
|
+
let value = obj;
|
|
3184
|
+
for (const key of keys) {
|
|
3185
|
+
if (value == null)
|
|
3186
|
+
return null;
|
|
3187
|
+
value = value[key];
|
|
3188
|
+
}
|
|
3189
|
+
return value;
|
|
3190
|
+
}
|
|
3191
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviTable, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
3192
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.1.2", type: HviTable, isStandalone: true, selector: "[hviTable]", inputs: { zebra: ["zebra", "zebra", booleanAttribute], border: ["border", "border", booleanAttribute], hover: ["hover", "hover", booleanAttribute], stickyHeader: ["stickyHeader", "stickyHeader", booleanAttribute], value: "value", sortField: "sortField", sortOrder: "sortOrder", globalFilterFields: "globalFilterFields", paginator: ["paginator", "paginator", booleanAttribute], rows: ["rows", "rows", numberAttribute], first: ["first", "first", numberAttribute] }, outputs: { sortChange: "sortChange", pageChange: "pageChange", currentPageChange: "currentPageChange" }, host: { properties: { "attr.data-zebra": "zebra || null", "attr.data-border": "border || null", "attr.data-hover": "hover || null", "attr.data-sticky-header": "stickyHeader || null" }, classAttribute: "ds-table" }, exportAs: ["hviTable"], ngImport: i0 });
|
|
3193
|
+
}
|
|
3194
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviTable, decorators: [{
|
|
3195
|
+
type: Directive,
|
|
3196
|
+
args: [{
|
|
3197
|
+
selector: '[hviTable]',
|
|
3198
|
+
standalone: true,
|
|
3199
|
+
exportAs: 'hviTable',
|
|
3200
|
+
host: {
|
|
3201
|
+
class: 'ds-table',
|
|
3202
|
+
'[attr.data-zebra]': 'zebra || null',
|
|
3203
|
+
'[attr.data-border]': 'border || null',
|
|
3204
|
+
'[attr.data-hover]': 'hover || null',
|
|
3205
|
+
'[attr.data-sticky-header]': 'stickyHeader || null',
|
|
3206
|
+
},
|
|
3207
|
+
}]
|
|
3208
|
+
}], propDecorators: { zebra: [{
|
|
3209
|
+
type: Input,
|
|
3210
|
+
args: [{ transform: booleanAttribute }]
|
|
3211
|
+
}], border: [{
|
|
3212
|
+
type: Input,
|
|
3213
|
+
args: [{ transform: booleanAttribute }]
|
|
3214
|
+
}], hover: [{
|
|
3215
|
+
type: Input,
|
|
3216
|
+
args: [{ transform: booleanAttribute }]
|
|
3217
|
+
}], stickyHeader: [{
|
|
3218
|
+
type: Input,
|
|
3219
|
+
args: [{ transform: booleanAttribute }]
|
|
3220
|
+
}], value: [{
|
|
3221
|
+
type: Input
|
|
3222
|
+
}], sortField: [{
|
|
3223
|
+
type: Input
|
|
3224
|
+
}], sortOrder: [{
|
|
3225
|
+
type: Input
|
|
3226
|
+
}], globalFilterFields: [{
|
|
3227
|
+
type: Input
|
|
3228
|
+
}], paginator: [{
|
|
3229
|
+
type: Input,
|
|
3230
|
+
args: [{ transform: booleanAttribute }]
|
|
3231
|
+
}], rows: [{
|
|
3232
|
+
type: Input,
|
|
3233
|
+
args: [{ transform: numberAttribute }]
|
|
3234
|
+
}], first: [{
|
|
3235
|
+
type: Input,
|
|
3236
|
+
args: [{ transform: numberAttribute }]
|
|
3237
|
+
}], sortChange: [{
|
|
3238
|
+
type: Output
|
|
3239
|
+
}], pageChange: [{
|
|
3240
|
+
type: Output
|
|
3241
|
+
}], currentPageChange: [{
|
|
3242
|
+
type: Output
|
|
3243
|
+
}] } });
|
|
3244
|
+
|
|
3245
|
+
/**
|
|
3246
|
+
* @summary
|
|
3247
|
+
* Directive for sorterbare tabell-header celler.
|
|
3248
|
+
* Kommuniserer automatisk med parent HviTable for å håndtere sortering.
|
|
3249
|
+
*
|
|
3250
|
+
* @example
|
|
3251
|
+
* ```html
|
|
3252
|
+
* <table hviTable [value]="persons" #table>
|
|
3253
|
+
* <thead>
|
|
3254
|
+
* <tr>
|
|
3255
|
+
* <th hviSortableColumn="navn">
|
|
3256
|
+
* <button>Navn</button>
|
|
3257
|
+
* </th>
|
|
3258
|
+
* <th>Epost</th>
|
|
3259
|
+
* </tr>
|
|
3260
|
+
* </thead>
|
|
3261
|
+
* <tbody>
|
|
3262
|
+
* @for (person of table.sortedValue(); track person.id) {
|
|
3263
|
+
* <tr>
|
|
3264
|
+
* <td>{{ person.navn }}</td>
|
|
3265
|
+
* <td>{{ person.epost }}</td>
|
|
3266
|
+
* </tr>
|
|
3267
|
+
* }
|
|
3268
|
+
* </tbody>
|
|
3269
|
+
* </table>
|
|
3270
|
+
* ```
|
|
3271
|
+
*
|
|
3272
|
+
* @see {@link https://designsystemet.no/no/components/docs/table/code}
|
|
3273
|
+
*/
|
|
3274
|
+
class HviSortableColumn {
|
|
3275
|
+
table = inject(HviTable, { optional: true });
|
|
3276
|
+
/**
|
|
3277
|
+
* Feltet som denne kolonnen sorterer etter.
|
|
3278
|
+
* Må matche property-navn i data-objektene.
|
|
3279
|
+
*/
|
|
3280
|
+
field;
|
|
3281
|
+
/**
|
|
3282
|
+
* Henter sorteringsretning fra parent table.
|
|
3283
|
+
*/
|
|
3284
|
+
get sortDirection() {
|
|
3285
|
+
return this.table?.getSortDirection(this.field) ?? 'none';
|
|
3286
|
+
}
|
|
3287
|
+
onClick(event) {
|
|
3288
|
+
const target = event.target;
|
|
3289
|
+
// Bare håndter klikk på button inne i th
|
|
3290
|
+
if (target.tagName === 'BUTTON' || target.closest('button')) {
|
|
3291
|
+
this.table?.sort(this.field);
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviSortableColumn, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
3295
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.2", type: HviSortableColumn, isStandalone: true, selector: "th[hviSortableColumn]", inputs: { field: ["hviSortableColumn", "field"] }, host: { listeners: { "click": "onClick($event)" }, properties: { "attr.aria-sort": "sortDirection" } }, ngImport: i0 });
|
|
3296
|
+
}
|
|
3297
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImport: i0, type: HviSortableColumn, decorators: [{
|
|
3298
|
+
type: Directive,
|
|
3299
|
+
args: [{
|
|
3300
|
+
selector: 'th[hviSortableColumn]',
|
|
3301
|
+
standalone: true,
|
|
3302
|
+
host: {
|
|
3303
|
+
'[attr.aria-sort]': 'sortDirection',
|
|
3304
|
+
},
|
|
3305
|
+
}]
|
|
3306
|
+
}], propDecorators: { field: [{
|
|
3307
|
+
type: Input,
|
|
3308
|
+
args: [{ required: true, alias: 'hviSortableColumn' }]
|
|
3309
|
+
}], onClick: [{
|
|
3310
|
+
type: HostListener,
|
|
3311
|
+
args: ['click', ['$event']]
|
|
3312
|
+
}] } });
|
|
3313
|
+
|
|
2611
3314
|
/**
|
|
2612
3315
|
* TabPanel inneholder innholdet som vises når en tilhørende Tab er aktiv.
|
|
2613
3316
|
*
|
|
@@ -3386,5 +4089,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.2", ngImpor
|
|
|
3386
4089
|
* Generated bundle index. Do not edit.
|
|
3387
4090
|
*/
|
|
3388
4091
|
|
|
3389
|
-
export { HviAlert, HviAvatar, HviBadge, HviBadgePosition, HviBreadcrumbs, HviButton, HviCard, HviCardBlock, HviChipButton, HviChipLabel, HviControlInvalid, HviDetails, HviDetailsContent, HviDetailsSummary, HviDialog, HviDialogBlock, HviDivider, HviErrorSummary, HviField, HviFieldAffix, HviFieldAffixes, HviFieldCounter, HviFieldDescription, HviFieldKit, HviFieldOptional, HviFieldValidation, HviFieldset, HviForm, HviForms, HviHeading, HviIcon, HviInput, HviLabel, HviLink, HviList, HviParagraph, HviPopover, HviSearch, HviSearchClear, HviSelect, HviSkeleton, HviSkipLink, HviSpinner, HviTab, HviTabPanel, HviTabs, HviTag, HviToggleGroup, HviToggleGroupItem, HviToggleGroupItemToken, HviTooltip, HviValidationKit, HviValidationMessage, fieldObserver, hviCustom, hviEmail, hviExtractMessages, hviExtractValidators, hviMax, hviMaxLength, hviMin, hviMinLength, hviNullValidator, hviPattern, hviRequired, hviRequiredTrue, hviValidators, isElement, isInputLike, isLabel };
|
|
4092
|
+
export { HviAlert, HviAvatar, HviBadge, HviBadgePosition, HviBreadcrumbs, HviButton, HviCard, HviCardBlock, HviChipButton, HviChipLabel, HviControlInvalid, HviDetails, HviDetailsContent, HviDetailsSummary, HviDialog, HviDialogBlock, HviDivider, HviErrorSummary, HviField, HviFieldAffix, HviFieldAffixes, HviFieldCounter, HviFieldDescription, HviFieldKit, HviFieldOptional, HviFieldValidation, HviFieldset, HviForm, HviForms, HviHeading, HviIcon, HviInput, HviLabel, HviLink, HviList, HviPagination, HviParagraph, HviPopover, HviSearch, HviSearchClear, HviSelect, HviSkeleton, HviSkipLink, HviSortableColumn, HviSpinner, HviTab, HviTabPanel, HviTable, HviTabs, HviTag, HviToggleGroup, HviToggleGroupItem, HviToggleGroupItemToken, HviTooltip, HviValidationKit, HviValidationMessage, fieldObserver, hviCustom, hviEmail, hviExtractMessages, hviExtractValidators, hviMax, hviMaxLength, hviMin, hviMinLength, hviNullValidator, hviPattern, hviRequired, hviRequiredTrue, hviValidators, isElement, isInputLike, isLabel };
|
|
3390
4093
|
//# sourceMappingURL=helsevestikt-hviktor-angular.mjs.map
|