@sb1/indeks-web 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/dist/npm/index.d.ts +76 -0
- package/dist/npm/index.js +1807 -6
- package/dist/npm/lib/components/checkbox-group/IxCheckboxGroup.d.ts +83 -0
- package/dist/npm/lib/components/combobox/IxCombobox.d.ts +132 -0
- package/dist/npm/lib/components/field/IxField.d.ts +50 -78
- package/dist/npm/lib/components/field/formats.d.ts +101 -0
- package/dist/npm/lib/components/progress-bar/IxProgressBar.d.ts +56 -0
- package/dist/npm/lib/components/tabs/IxTabs.d.ts +121 -0
- package/dist/npm/lib/modal/modal.d.ts +1 -0
- package/dist/npm/lib/utils/platform.d.ts +15 -0
- package/package.json +5 -5
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IxCheckboxGroup — ARIA-lim for checkbox-grupper.
|
|
3
|
+
*
|
|
4
|
+
* ## Designfilosofi
|
|
5
|
+
*
|
|
6
|
+
* Komponenten erstatter ingen native HTML-elementer. Forfatteren skriver
|
|
7
|
+
* native <input type="checkbox">, <label> og hjelpetekster inni
|
|
8
|
+
* <ix-checkbox-group>. Komponenten sin eneste jobb er å sette opp
|
|
9
|
+
* ARIA-koblingene mellom dem. Speiler <ix-radio-group>, med tre bevisste
|
|
10
|
+
* forskjeller (se under).
|
|
11
|
+
*
|
|
12
|
+
* ## Hva komponenten gjør
|
|
13
|
+
*
|
|
14
|
+
* 1. Setter role="group" på host slik at skjermlesere annonserer gruppen og
|
|
15
|
+
* leser legend-teksten som gruppens tilgjengelige navn. (Det finnes ingen
|
|
16
|
+
* "checkboxgroup"-rolle i ARIA — "group" er korrekt for en checkbox-gruppe.)
|
|
17
|
+
*
|
|
18
|
+
* 2. Kobler legend via aria-labelledby — genererer ID på [data-field="legend"]
|
|
19
|
+
* om nødvendig og peker host til den.
|
|
20
|
+
*
|
|
21
|
+
* 3. Setter aria-describedby på host med IDer til description og error
|
|
22
|
+
* (description alltid først). Hopper over elementer som mangler.
|
|
23
|
+
*
|
|
24
|
+
* 4. Genererer stabile IDer på hvert input og setter htmlFor på matchende label
|
|
25
|
+
* om label ikke allerede har det. Respekterer alltid eksisterende IDer.
|
|
26
|
+
*
|
|
27
|
+
* 5. Setter aria-live="polite" på [data-field="error"] og observerer
|
|
28
|
+
* textContent-endringer: ikke-tomt innhold → aria-invalid="true" på host;
|
|
29
|
+
* tomt → aria-invalid fjernes. aria-invalid settes kun på host.
|
|
30
|
+
*
|
|
31
|
+
* 6. Propagerer disabled/readonly til alle inputs via attributeChangedCallback.
|
|
32
|
+
* Per-knapp disabled bevares — settes med en WeakMap ved første _wire().
|
|
33
|
+
*
|
|
34
|
+
* 7. Lytter på childList på host slik at nye <input type="checkbox">-elementer
|
|
35
|
+
* som legges til etter mount automatisk wires (id, name, htmlFor, disabled).
|
|
36
|
+
*
|
|
37
|
+
* ## Forskjeller fra <ix-radio-group>
|
|
38
|
+
*
|
|
39
|
+
* - **role="group"**, ikke "radiogroup".
|
|
40
|
+
* - **Ingen name-mutex.** Checkbox deler ikke `name` for gjensidig utelukkelse.
|
|
41
|
+
* Host-`name` propageres til alle inputs (for form-innsending under felles
|
|
42
|
+
* navn), men når host ikke har name lar vi forfatterens egne name/value stå
|
|
43
|
+
* — vi auto-genererer ikke et felles name.
|
|
44
|
+
* - **Ingen required-triks.** For radio settes `required` kun på første input
|
|
45
|
+
* (Safari-boble-fiks for mutex-gruppen). For checkbox betyr `required` "denne
|
|
46
|
+
* må krysses", så vi rører ikke required automatisk på gruppenivå.
|
|
47
|
+
* - **Tastatur:** checkbox har ingen pil-navigasjon; readonly blokkerer kun
|
|
48
|
+
* Space (toggle) — ikke piltaster.
|
|
49
|
+
*
|
|
50
|
+
* ## Ingen Shadow DOM
|
|
51
|
+
*
|
|
52
|
+
* Komponenten bruker light DOM slik at CSS fra @sb1/indeks-css når alle
|
|
53
|
+
* delelementer direkte.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* <ix-checkbox-group>
|
|
57
|
+
* <span data-field="legend">Hvordan vil du bli kontaktet?</span>
|
|
58
|
+
* <span data-field="description">Velg én eller flere</span>
|
|
59
|
+
* <div data-field="items">
|
|
60
|
+
* <div class="ix-checkbox">
|
|
61
|
+
* <input type="checkbox" value="epost" />
|
|
62
|
+
* <label>E-post</label>
|
|
63
|
+
* </div>
|
|
64
|
+
* </div>
|
|
65
|
+
* <span data-field="error"></span>
|
|
66
|
+
* </ix-checkbox-group>
|
|
67
|
+
*/
|
|
68
|
+
export declare class IxCheckboxGroup extends HTMLElement {
|
|
69
|
+
private _errorObserver;
|
|
70
|
+
private _childObserver;
|
|
71
|
+
private _keydownListener;
|
|
72
|
+
private _instanceId;
|
|
73
|
+
private _ownDisabled;
|
|
74
|
+
static get observedAttributes(): string[];
|
|
75
|
+
connectedCallback(): void;
|
|
76
|
+
disconnectedCallback(): void;
|
|
77
|
+
attributeChangedCallback(attr: string, _oldValue: string | null, _newValue: string | null): void;
|
|
78
|
+
private _wire;
|
|
79
|
+
private _containsCheckboxInput;
|
|
80
|
+
private _wireInputs;
|
|
81
|
+
private _syncInvalid;
|
|
82
|
+
private _syncHostStateToInputs;
|
|
83
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IxCombobox — ARIA-lim for en søkbar select med single- og multiselect.
|
|
3
|
+
*
|
|
4
|
+
* ## Designfilosofi
|
|
5
|
+
*
|
|
6
|
+
* Komponenten erstatter ingen native elementer. Forfatteren skriver <input>,
|
|
7
|
+
* en options-liste og en skjult <select> i light DOM inni <ix-combobox>.
|
|
8
|
+
* Komponentens jobb er å koble dem sammen med korrekt combobox/listbox-ARIA,
|
|
9
|
+
* håndtere tastatur og filtrering, posisjonere lista, og synkronisere valgte
|
|
10
|
+
* verdier til den skjulte <select> for form-innsending.
|
|
11
|
+
*
|
|
12
|
+
* ## Virtual focus
|
|
13
|
+
*
|
|
14
|
+
* DOM-fokus blir ALLTID på <input>. Aktivt listeelement spores med
|
|
15
|
+
* `aria-activedescendant` som peker på option-ens id — fokus flyttes aldri
|
|
16
|
+
* fysisk inn i lista. Dette lar brukeren skrive og pil-navigere samtidig, og
|
|
17
|
+
* er den enkeltbeslutningen hjemmelagde comboboxer oftest bommer på.
|
|
18
|
+
*
|
|
19
|
+
* ## Single vs. multi
|
|
20
|
+
*
|
|
21
|
+
* `multiple` (attributt på host) bytter oppførsel — ikke to komponenter:
|
|
22
|
+
* - Single: valg setter input.value = label, lukker lista, synker én <option>.
|
|
23
|
+
* - Multi: valg legges i et sett, lista holdes åpen, input tømmes, og en
|
|
24
|
+
* <data class="ix-chip" data-removable> rendres i chips-wrapperen.
|
|
25
|
+
* Chips nås med piltast (radiogruppe-modell), ikke egne tab-stopp.
|
|
26
|
+
*
|
|
27
|
+
* ## Posisjonering
|
|
28
|
+
*
|
|
29
|
+
* Lista posisjoneres manuelt med getBoundingClientRect + viewport-clamp (samme
|
|
30
|
+
* grep som tooltip.ts) — position: fixed, flip over feltet ved plassmangel.
|
|
31
|
+
* INGEN popover-API og INGEN avhengigheter: indeks-web er dependency-fritt og
|
|
32
|
+
* bundles til CDN, og manuell posisjonering fungerer på hele browserslist.
|
|
33
|
+
*
|
|
34
|
+
* ## Delelementer (data-field / klasse i light DOM)
|
|
35
|
+
* - .ix-text-field > input → combobox-input
|
|
36
|
+
* - .ix-combobox__toggle → chevron-knapp (åpne/lukke)
|
|
37
|
+
* - .ix-combobox__listbox → listbox-panel
|
|
38
|
+
* - .ix-combobox__option[data-value] → alternativer
|
|
39
|
+
* - .ix-combobox__no-hits → melding ved 0 treff (role="status")
|
|
40
|
+
* - [data-field="chips"] → chips-wrapper (kun multi)
|
|
41
|
+
* - [data-field="native"] (select) → skjult select for form-synk
|
|
42
|
+
*
|
|
43
|
+
* @example Single
|
|
44
|
+
* <ix-combobox data-no-hits-text="Ingen treff">
|
|
45
|
+
* <div class="ix-text-field">
|
|
46
|
+
* <input class="ix-text-field__input" placeholder="Søk …" />
|
|
47
|
+
* <button class="ix-combobox__toggle"></button>
|
|
48
|
+
* </div>
|
|
49
|
+
* <div class="ix-combobox__listbox">
|
|
50
|
+
* <div class="ix-combobox__option" data-value="47">
|
|
51
|
+
* <span class="ix-combobox__option-check" aria-hidden="true"></span>
|
|
52
|
+
* <span class="ix-combobox__option-label">Norge</span>
|
|
53
|
+
* </div>
|
|
54
|
+
* </div>
|
|
55
|
+
* <div class="ix-combobox__no-hits" role="status" hidden>Ingen treff</div>
|
|
56
|
+
* <select data-field="native" hidden></select>
|
|
57
|
+
* </ix-combobox>
|
|
58
|
+
*/
|
|
59
|
+
export declare class IxCombobox extends HTMLElement {
|
|
60
|
+
private _instanceId;
|
|
61
|
+
private _input;
|
|
62
|
+
private _listbox;
|
|
63
|
+
private _toggle;
|
|
64
|
+
private _chips;
|
|
65
|
+
private _noHits;
|
|
66
|
+
private _noHitsText;
|
|
67
|
+
private _select;
|
|
68
|
+
private _arrowHint;
|
|
69
|
+
private _results;
|
|
70
|
+
private _lastAnnouncedCount;
|
|
71
|
+
private _announceTimer;
|
|
72
|
+
private _activeOption;
|
|
73
|
+
private _onInput;
|
|
74
|
+
private _onKeydown;
|
|
75
|
+
private _onFocusin;
|
|
76
|
+
private _onClick;
|
|
77
|
+
private _onDocPointer;
|
|
78
|
+
private _onReposition;
|
|
79
|
+
private _optionObserver;
|
|
80
|
+
static get observedAttributes(): string[];
|
|
81
|
+
connectedCallback(): void;
|
|
82
|
+
disconnectedCallback(): void;
|
|
83
|
+
attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void;
|
|
84
|
+
get multiple(): boolean;
|
|
85
|
+
set multiple(value: boolean);
|
|
86
|
+
private get _options();
|
|
87
|
+
private get _visibleOptions();
|
|
88
|
+
private get _isDisabled();
|
|
89
|
+
private _wire;
|
|
90
|
+
private _rewireOptions;
|
|
91
|
+
private _normalizeSelectedAria;
|
|
92
|
+
private get _isOpen();
|
|
93
|
+
private _open;
|
|
94
|
+
private _close;
|
|
95
|
+
private _toggleOpen;
|
|
96
|
+
private _position;
|
|
97
|
+
private _addRepositionListeners;
|
|
98
|
+
private _removeRepositionListeners;
|
|
99
|
+
private _handleInput;
|
|
100
|
+
private _filter;
|
|
101
|
+
private _searchTexts;
|
|
102
|
+
private _match;
|
|
103
|
+
private _updateNoHits;
|
|
104
|
+
private _optionLabelText;
|
|
105
|
+
private _optionDescriptionText;
|
|
106
|
+
private _setActive;
|
|
107
|
+
private _moveActive;
|
|
108
|
+
private _handleKeydown;
|
|
109
|
+
private _handleInputKeydown;
|
|
110
|
+
private _handleChipKeydown;
|
|
111
|
+
private _handleClick;
|
|
112
|
+
private _selectOption;
|
|
113
|
+
private _deselectValue;
|
|
114
|
+
private _syncFromInitialSelection;
|
|
115
|
+
private _chipElements;
|
|
116
|
+
private _addChip;
|
|
117
|
+
private _removeChip;
|
|
118
|
+
private _chipLabel;
|
|
119
|
+
private _syncChipLabels;
|
|
120
|
+
private _syncSelect;
|
|
121
|
+
private _emitChange;
|
|
122
|
+
private _syncControlState;
|
|
123
|
+
private _syncMultiState;
|
|
124
|
+
private _syncChipsGroup;
|
|
125
|
+
private _syncArrowHint;
|
|
126
|
+
private _ensureArrowHintEl;
|
|
127
|
+
private _addDescribedBy;
|
|
128
|
+
private _removeDescribedBy;
|
|
129
|
+
private _ensureResultsEl;
|
|
130
|
+
private _announceResults;
|
|
131
|
+
private _resetResults;
|
|
132
|
+
}
|
|
@@ -1,86 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
* IxField — tilgjengelighetslim mellom label, input og hjelpetekster.
|
|
3
|
-
*
|
|
4
|
-
* ## Designfilosofi
|
|
5
|
-
*
|
|
6
|
-
* Komponenten erstatter ingen native HTML-elementer. `<label>`, `<input>`,
|
|
7
|
-
* `<select>`, `<textarea>` og hjelpetekster skrives som vanlig HTML inne i
|
|
8
|
-
* `<ix-field>`. Komponenten sin eneste jobb er å sette opp ARIA-koblingene
|
|
9
|
-
* mellom dem — arbeid som er kjedelig å gjøre manuelt og lett å gjøre feil.
|
|
10
|
-
*
|
|
11
|
-
* Denne tilnærmingen betyr at:
|
|
12
|
-
* - Native nettleser-atferd, validering og styling forblir intakt.
|
|
13
|
-
* - Alle form-attributter (`required`, `disabled`, `name`, `value`, osv.)
|
|
14
|
-
* settes direkte på native elementet — ikke som props til en wrapper.
|
|
15
|
-
* - Komponenten kan brukes med alle typer input uten kodeendringer.
|
|
16
|
-
*
|
|
17
|
-
* ## Hva komponenten gjør
|
|
18
|
-
*
|
|
19
|
-
* 1. Kobler `<label>` til input via `for`/`id`-paret.
|
|
20
|
-
* Genererer en stabil ID på input om den mangler en.
|
|
21
|
-
*
|
|
22
|
-
* 2. Setter `aria-describedby` på input med IDer til description og
|
|
23
|
-
* error-elementet, slik at skjermlesere leser dem opp etter label og verdi.
|
|
24
|
-
*
|
|
25
|
-
* 3. Setter `aria-live="polite"` på error-elementet.
|
|
26
|
-
* Error-elementet skal alltid ligge i DOM — tomt betyr ingen feil.
|
|
27
|
-
* Dette er et krav fra ARIA Live Regions: elementet må eksistere i DOM
|
|
28
|
-
* *før* innholdet settes inn for at skjermlesere skal fange opp endringen.
|
|
29
|
-
* Hadde vi injisert elementet dynamisk ville mange skjermlesere gått glipp
|
|
30
|
-
* av meldingen.
|
|
31
|
-
*
|
|
32
|
-
* Vi bruker *ikke* `role="alert"` fordi det impliserer `aria-live="assertive"`
|
|
33
|
-
* som avbryter brukeren. Feilmeldinger i skjema er viktige, men ikke så
|
|
34
|
-
* kritiske at de bør avbryte — `polite` venter til skjermleseren er ferdig.
|
|
35
|
-
*
|
|
36
|
-
* 4. Bruker MutationObserver på error-elementet. Når innholdet endres:
|
|
37
|
-
* - Ikke-tomt → `aria-invalid="true"` på input (visuell og semantisk feil)
|
|
38
|
-
* - Tomt → `aria-invalid` fjernes
|
|
39
|
-
* Applikasjonen styrer altså feilmelding ved å sette/tømme textContent —
|
|
40
|
-
* komponenten holder input-tilstanden synkronisert automatisk.
|
|
41
|
-
*
|
|
42
|
-
* ## Identifisering av delelementer
|
|
43
|
-
*
|
|
44
|
-
* Komponenten finner delelementer via:
|
|
45
|
-
* - `<label>` → første label-element
|
|
46
|
-
* - `input | select | textarea` → første native form-kontroll
|
|
47
|
-
* - `[data-field="description"]` → hjelpetekst
|
|
48
|
-
* - `[data-field="error"]` → feilmelding (alltid til stede, kan være tom)
|
|
49
|
-
* - `[data-field="prefix"]` → visuell prefix (skjules med aria-hidden)
|
|
50
|
-
* - `[data-field="suffix"]` → visuell suffix (skjules med aria-hidden)
|
|
51
|
-
*
|
|
52
|
-
* `data-field` brukes fremfor `slot`-attributtet selv om Shadow DOM ikke er i
|
|
53
|
-
* bruk. `slot` er et reservert nøkkelord knytt til Shadow DOM og ville vært
|
|
54
|
-
* misvisende, samt kunne krasje om `<ix-field>` senere pakkes inn i en
|
|
55
|
-
* komponent med Shadow DOM.
|
|
56
|
-
*
|
|
57
|
-
* ## Ingen Shadow DOM
|
|
58
|
-
*
|
|
59
|
-
* Komponenten bruker light DOM. Det betyr at CSS fra `@sb1/indeks-css` når
|
|
60
|
-
* alle delelementer uten videre, og at ARIA-attributtene er fullt synlige i
|
|
61
|
-
* den tilgjengelighetstreet nettleseren eksponerer til hjelpemiddelteknologi.
|
|
62
|
-
*
|
|
63
|
-
* @example Grunnleggende bruk
|
|
64
|
-
* <ix-field>
|
|
65
|
-
* <label>E-postadresse</label>
|
|
66
|
-
* <span data-field="description">Vi sender kvittering til denne adressen</span>
|
|
67
|
-
* <input type="email" name="email" />
|
|
68
|
-
* <span data-field="error"></span>
|
|
69
|
-
* </ix-field>
|
|
70
|
-
*
|
|
71
|
-
* @example Sett feilmelding programmatisk
|
|
72
|
-
* const errorEl = document.querySelector('ix-field [data-field="error"]');
|
|
73
|
-
* errorEl.textContent = 'Oppgi en gyldig e-postadresse';
|
|
74
|
-
* // → input får automatisk aria-invalid="true"
|
|
75
|
-
*
|
|
76
|
-
* errorEl.textContent = '';
|
|
77
|
-
* // → aria-invalid fjernes
|
|
78
|
-
*/
|
|
1
|
+
import { registerFormat, FieldFormatter } from './formats.js';
|
|
79
2
|
export declare class IxField extends HTMLElement {
|
|
80
3
|
private _observer;
|
|
81
4
|
private _stateObserver;
|
|
82
5
|
private _charCountListener;
|
|
83
6
|
private _charCountControl;
|
|
7
|
+
private _formatterProp;
|
|
8
|
+
private _formatTeardown;
|
|
9
|
+
private _activeFormatter;
|
|
10
|
+
private _activeControl;
|
|
11
|
+
private _formatMirror;
|
|
12
|
+
/**
|
|
13
|
+
* Registrer en navngitt formatter globalt. Brukes deretter via
|
|
14
|
+
* `data-format="<navn>"` på et hvilket som helst input inne i et ix-field.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* IxField.registerFormatter('orgnr', { format, parse });
|
|
18
|
+
* // <ix-field><input data-format="orgnr" …></ix-field>
|
|
19
|
+
*/
|
|
20
|
+
static registerFormatter: typeof registerFormat;
|
|
21
|
+
/**
|
|
22
|
+
* Sett en formatter direkte på instansen (høyest presedens). Lar konsumenten
|
|
23
|
+
* gi vilkårlig format/parse-logikk som ikke kan uttrykkes som attributt.
|
|
24
|
+
*/
|
|
25
|
+
set formatter(value: FieldFormatter | null);
|
|
26
|
+
get formatter(): FieldFormatter | null;
|
|
27
|
+
/**
|
|
28
|
+
* Den rå (uformaterte) verdien til feltet. Sannheten ligger i den skjulte
|
|
29
|
+
* mirror-inputen; uten mirror (feltet mangler `name`) utledes den ved å
|
|
30
|
+
* `parse()` den synlige verdien. Tom streng når ingen formatter er aktiv
|
|
31
|
+
* eller det ikke finnes noe input.
|
|
32
|
+
*
|
|
33
|
+
* Praktisk for ren JS/HTML: `document.querySelector('ix-field').rawValue`.
|
|
34
|
+
* Merk at `<form>`-innsending og `new FormData(form)` også gir rå verdi
|
|
35
|
+
* automatisk, siden mirror-inputen bærer det opprinnelige `name`-et.
|
|
36
|
+
*/
|
|
37
|
+
get rawValue(): string;
|
|
38
|
+
/** True når den aktive formatteren formaterer live (mens man skriver). */
|
|
39
|
+
get live(): boolean;
|
|
84
40
|
static get observedAttributes(): string[];
|
|
85
41
|
connectedCallback(): void;
|
|
86
42
|
disconnectedCallback(): void;
|
|
@@ -89,6 +45,22 @@ export declare class IxField extends HTMLElement {
|
|
|
89
45
|
private _setupCharCount;
|
|
90
46
|
private _teardownCharCount;
|
|
91
47
|
private _updateCharCount;
|
|
48
|
+
private _resolveFormatter;
|
|
49
|
+
private _applyLiveOverride;
|
|
50
|
+
private _wireFormatting;
|
|
51
|
+
private _maskInPlace;
|
|
52
|
+
/**
|
|
53
|
+
* Re-anvender formateringen fra en rå verdi.
|
|
54
|
+
*
|
|
55
|
+
* Nyttig for controlled React: når React skriver den rå prop-verdien ved en
|
|
56
|
+
* re-render fyres ikke `input`-eventet, så visningen må oppdateres eksplisitt.
|
|
57
|
+
* Kall denne etter commit (f.eks. i `useLayoutEffect`). No-op når ingen
|
|
58
|
+
* formatter er aktiv.
|
|
59
|
+
*
|
|
60
|
+
* @param raw Ny rå verdi. Utelates den, reformateres gjeldende rå verdi.
|
|
61
|
+
*/
|
|
62
|
+
refreshFormat(raw?: string): void;
|
|
63
|
+
private _teardownFormatting;
|
|
92
64
|
private _syncInvalid;
|
|
93
65
|
private _syncControlState;
|
|
94
66
|
private _syncTooltip;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input-formatering for ix-field — rene funksjoner, pattern-kompilator og registry.
|
|
3
|
+
*
|
|
4
|
+
* ## Filosofi: vi formaterer, vi masker ikke
|
|
5
|
+
*
|
|
6
|
+
* Dette er IKKE en tegn-maske som avviser tastetrykk. Uansett modus vises ALT
|
|
7
|
+
* brukeren skriver — også ekstra tegn og tegn som ikke «hører hjemme» (en bokstav
|
|
8
|
+
* i et sifferfelt). Feil fanges av validering, ikke ved å droppe tegn. Derfor er
|
|
9
|
+
* `parse` tapsfri (fjerner kun separatorene `format` setter inn).
|
|
10
|
+
*
|
|
11
|
+
* To moduser, styrt av `live`-flagget på formatteren:
|
|
12
|
+
* - **Blur (standard):** feltet formateres når det mister fokus og viser rå
|
|
13
|
+
* (redigerbar) verdi ved fokus. Ingen caret-hopp mens man skriver — en bevisst
|
|
14
|
+
* a11y-beslutning (jf. GOV.UK-frarådingen mot å reformatere telefonnummer mens
|
|
15
|
+
* brukeren skriver).
|
|
16
|
+
* - **Live (opt-in, `live: true`):** separatorene bygger seg opp mens man
|
|
17
|
+
* skriver. De innebygde variantene bruker dette.
|
|
18
|
+
*
|
|
19
|
+
* ## Kontrakten
|
|
20
|
+
*
|
|
21
|
+
* En formatter er et par rene funksjoner:
|
|
22
|
+
* - `format(raw)` : rå verdi → visningsstreng ("12345678" → "123 45 678")
|
|
23
|
+
* - `parse(vist)` : visningsstreng → rå verdi ("123 45 678" → "12345678")
|
|
24
|
+
*
|
|
25
|
+
* Den **rå verdien** er alltid sannheten. Konsumenten lagrer og validerer den rå
|
|
26
|
+
* verdien; visningsstrengen er kun presentasjon. `parse(format(raw)) === raw` skal
|
|
27
|
+
* holde for enhver gyldig rå verdi.
|
|
28
|
+
*
|
|
29
|
+
* ## Tre måter å skaffe en formatter (i ix-field, se IxField.ts)
|
|
30
|
+
*
|
|
31
|
+
* 1. Egen funksjon som property: `el.formatter = { format, parse }`
|
|
32
|
+
* 2. Navngitt via registry: `<input data-format="phone">` (+ `registerFormat(...)`)
|
|
33
|
+
* 3. Pattern-streng, ingen JS: `<input data-format-pattern="000 00 000">`
|
|
34
|
+
*
|
|
35
|
+
* Alle tre gir samme `FieldFormatter`-kontrakt.
|
|
36
|
+
*/
|
|
37
|
+
export interface FieldFormatter {
|
|
38
|
+
/** Rå verdi → visningsstreng. */
|
|
39
|
+
format(raw: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Visningsstreng → rå verdi (sannheten konsumenten lagrer).
|
|
42
|
+
*
|
|
43
|
+
* **Tapsfri:** fjerner kun separatorene formatteren selv setter inn og
|
|
44
|
+
* beholder alt annet — også tegn som ikke «hører hjemme» (en bokstav i et
|
|
45
|
+
* sifferfelt). Vi formaterer, vi masker ikke: feil fanges av validering, ikke
|
|
46
|
+
* ved å droppe tegn. Derfor holder `parse(format(raw)) === raw` for enhver rå
|
|
47
|
+
* verdi, ikke bare gyldige.
|
|
48
|
+
*/
|
|
49
|
+
parse(display: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Formater mens brukeren skriver (separatorer dukker opp fortløpende).
|
|
52
|
+
* Standard `false`/utelatt = format-on-blur (formateres når feltet mister
|
|
53
|
+
* fokus, rå verdi vises ved fokus for fri redigering). De innebygde variantene
|
|
54
|
+
* opter inn på live; egne pattern-strenger og `{format,parse}`-objekter er
|
|
55
|
+
* blur med mindre de setter dette.
|
|
56
|
+
*/
|
|
57
|
+
live?: boolean;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Lager en formatter fra en pattern-streng.
|
|
61
|
+
*
|
|
62
|
+
* `format` fyller slots med matchende tegn fra den rå verdien og setter inn
|
|
63
|
+
* separatorer først når neste slot faktisk fylles (ingen dinglende separator på
|
|
64
|
+
* slutten mens man skriver). Tegn som ikke passer neste slot — enten fordi
|
|
65
|
+
* brukeren skrev flere tegn enn patternet har plasser, eller skrev noe som ikke
|
|
66
|
+
* hører hjemme (bokstav i et sifferfelt) — droppes IKKE: den ledende delen som
|
|
67
|
+
* passer formateres, og resten legges uformatert på til slutt i samme
|
|
68
|
+
* rekkefølge brukeren skrev. Alt brukeren skriver forblir synlig.
|
|
69
|
+
*
|
|
70
|
+
* `parse` er tapsfri: den fjerner kun separator-tegnene patternet definerer
|
|
71
|
+
* (literal-tokens) og beholder alt annet i rekkefølge — ingen cap på antall
|
|
72
|
+
* slots, ingen filtrering av «ugyldige» tegn. Slik overlever et feilaktig tegn
|
|
73
|
+
* (bokstav i et sifferfelt) i den rå verdien og kan fanges av validering.
|
|
74
|
+
*/
|
|
75
|
+
export declare function createPatternFormatter(pattern: string): FieldFormatter;
|
|
76
|
+
/**
|
|
77
|
+
* Beløp er ikke en fast-lengde pattern-maske: lengden varierer og tusenskillet
|
|
78
|
+
* forskyver seg mens man skriver. Derfor en egen formatter.
|
|
79
|
+
*
|
|
80
|
+
* Rå verdi = siffer med valgfritt desimalskilletegn (`decimalSeparator`), uten
|
|
81
|
+
* tusenskille: `"1234567,89"`. Visning = med tusenskille: `"1 234 567,89"`.
|
|
82
|
+
*/
|
|
83
|
+
export declare function createAmountFormatter(options?: {
|
|
84
|
+
groupSeparator?: string;
|
|
85
|
+
decimalSeparator?: string;
|
|
86
|
+
}): FieldFormatter;
|
|
87
|
+
/**
|
|
88
|
+
* Utleder tusenskille og desimalskilletegn for en locale via `Intl.NumberFormat`.
|
|
89
|
+
* Brukes for locale-bevisst beløpsformatering uten å hardkode separatorer.
|
|
90
|
+
*/
|
|
91
|
+
export declare function amountFormatterForLocale(locale: string): FieldFormatter;
|
|
92
|
+
/**
|
|
93
|
+
* Registrerer en navngitt formatter som deretter kan brukes via
|
|
94
|
+
* `data-format="<navn>"` på et input inne i et ix-field, hvor som helst i appen.
|
|
95
|
+
*
|
|
96
|
+
* Team kan registrere sine egne varianter (orgnr, kortnummer, …) uten at
|
|
97
|
+
* designsystemet må endres — «felles, men ikke avhengig av oss».
|
|
98
|
+
*/
|
|
99
|
+
export declare function registerFormat(name: string, formatter: FieldFormatter): void;
|
|
100
|
+
/** Slår opp en registrert formatter. Returnerer `undefined` om navnet er ukjent. */
|
|
101
|
+
export declare function resolveFormat(name: string): FieldFormatter | undefined;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IxProgressBar — viser fremdrift i én sammenhengende prosess.
|
|
3
|
+
*
|
|
4
|
+
* DOM-generator: web componenten eier og genererer all indre HTML (header med
|
|
5
|
+
* label + verdi/statusikon, track + fill, og en støttetekst-region).
|
|
6
|
+
* Forfatteren skriver kun host-elementet `<ix-progress-bar>` med attributter —
|
|
7
|
+
* ingen intern struktur.
|
|
8
|
+
*
|
|
9
|
+
* Tilstander (`data-state`):
|
|
10
|
+
* - `active` (default): role="progressbar" med aria-valuenow/min/max på host.
|
|
11
|
+
* Nøytral/primær fremdriftsfarge. Ingen statusikon. Kan vise prosent.
|
|
12
|
+
* - `success` / `error`: rollen `progressbar` FJERNES (prosessen er ferdig/avbrutt).
|
|
13
|
+
* Status formidles semantisk «tilsvarende Message» — `data-status`
|
|
14
|
+
* (success/danger) kobler --ix-color-status-* og et dekorativt
|
|
15
|
+
* `<ix-icon data-badge>` vises alltid. Støtteteksten er en aria-live-region
|
|
16
|
+
* (role="status") så overgangen annonseres politely.
|
|
17
|
+
*
|
|
18
|
+
* Komponenten er rent informativ og aldri fokuserbar (setter aldri tabindex).
|
|
19
|
+
* Ugyldige verdier klampes: value < 0 → 0, > 100 → 100, ikke-numerisk → 0.
|
|
20
|
+
*
|
|
21
|
+
* iOS-workaround: VoiceOver leser ikke løpende `aria-valuenow`-endringer på
|
|
22
|
+
* `role="progressbar"`. På iOS byttes derfor rollen i `active` til `role="img"`
|
|
23
|
+
* og verdien bakes inn i `aria-label` (samme mønster som u-elements sin
|
|
24
|
+
* u-progress). Desktop beholder ekte progressbar-semantikk.
|
|
25
|
+
*/
|
|
26
|
+
export declare class IxProgressBar extends HTMLElement {
|
|
27
|
+
private _instanceId;
|
|
28
|
+
private _header;
|
|
29
|
+
private _label;
|
|
30
|
+
private _headerEnd;
|
|
31
|
+
private _track;
|
|
32
|
+
private _fill;
|
|
33
|
+
private _support;
|
|
34
|
+
private _supportText;
|
|
35
|
+
private _icon;
|
|
36
|
+
static get observedAttributes(): string[];
|
|
37
|
+
connectedCallback(): void;
|
|
38
|
+
attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null): void;
|
|
39
|
+
disconnectedCallback(): void;
|
|
40
|
+
/** Bygger den indre strukturen én gang (idempotent). */
|
|
41
|
+
private _ensureStructure;
|
|
42
|
+
private _update;
|
|
43
|
+
/**
|
|
44
|
+
* Plasserer statusikon og verdi. Følger Figma:
|
|
45
|
+
* - active + data-show-value → «NN %» i header-slutten.
|
|
46
|
+
* - success/error med label → ikon i header-slutten (til høyre for label).
|
|
47
|
+
* - success/error uten label → ikon foran støtteteksten.
|
|
48
|
+
*/
|
|
49
|
+
private _updateIconAndValue;
|
|
50
|
+
private _ensureIcon;
|
|
51
|
+
private _removeIcon;
|
|
52
|
+
private _resolveState;
|
|
53
|
+
/** Klamper til 0–100. Ikke-numerisk → 0. */
|
|
54
|
+
private _clampValue;
|
|
55
|
+
private _setAriaRef;
|
|
56
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IxTabs — ARIA-lim for faner (tabs) i light DOM.
|
|
3
|
+
*
|
|
4
|
+
* ## Designfilosofi
|
|
5
|
+
*
|
|
6
|
+
* Komponenten erstatter ingen native elementer. Forfatteren skriver semantisk
|
|
7
|
+
* HTML — en liste med faner og ett panel per fane — og <ix-tabs> kobler dem
|
|
8
|
+
* sammen med ARIA, roving tabindex, tastaturnavigasjon og panel-visning.
|
|
9
|
+
* Logikken er portet fra u-elements (u-tabs), men til Indeks' light-DOM-
|
|
10
|
+
* konvensjon (ingen Shadow DOM, ingen ::slotted).
|
|
11
|
+
*
|
|
12
|
+
* Fire custom elements samarbeider:
|
|
13
|
+
* - <ix-tabs> — rot og koordinator. Eier all kabling og tastatur.
|
|
14
|
+
* - <ix-tab-list> — role="tablist", horisontal orientering.
|
|
15
|
+
* - <ix-tab> — role="tab", én per fane.
|
|
16
|
+
* - <ix-tab-panel> — role="tabpanel", ett innholdspanel per fane.
|
|
17
|
+
*
|
|
18
|
+
* Underelementene setter kun sin egen `role` (så de fungerer isolert); <ix-tabs>
|
|
19
|
+
* er den autoritative koordinatoren og kjører ved connectedCallback.
|
|
20
|
+
*
|
|
21
|
+
* ## Hva <ix-tabs> gjør
|
|
22
|
+
*
|
|
23
|
+
* 1. Setter role="tablist" + aria-orientation="horizontal" på tab-lista,
|
|
24
|
+
* role="tab" på fanene og role="tabpanel" på panelene.
|
|
25
|
+
* 2. Genererer stabile IDer på faner og paneler (der de mangler) og setter opp
|
|
26
|
+
* kryssreferansene aria-controls (fane → panel) og aria-labelledby
|
|
27
|
+
* (panel → fane). Eksisterende IDer og aria-controls respekteres. Fane[i]
|
|
28
|
+
* kobles til panel[i], eller til panelet fanens aria-controls peker på.
|
|
29
|
+
* 3. Manuell aktivering: piltast (Venstre/Høyre) og Home/End flytter kun FOKUS
|
|
30
|
+
* via roving tabindex (fokusert fane får tabindex=0, resten -1).
|
|
31
|
+
* Piltast-navigasjonen looper rundt endene. Enter/Space (og klikk) aktiverer
|
|
32
|
+
* den fokuserte fanen.
|
|
33
|
+
* 4. Kun én fane er valgt om gangen. Ved aktivering flyttes aria-selected +
|
|
34
|
+
* tabindex til ny fane, tilhørende panel vises, øvrige skjules ([hidden] +
|
|
35
|
+
* aria-hidden). Panelet gjøres ikke fokuserbart — det er ikke en egen
|
|
36
|
+
* tab-stopp; innholdet i panelet bærer sin egen fokusrekkefølge.
|
|
37
|
+
* Fane→panel-kobling skjer via delt data-value (ellers aria-controls,
|
|
38
|
+
* ellers posisjon) — se _getPanel.
|
|
39
|
+
* 5. Minst én fane er valgt ved mount — enten den som har aria-selected="true"
|
|
40
|
+
* i markup, ellers den første fanen.
|
|
41
|
+
* 6. Ved aktivering (klikk/tastatur) sendes et bubbling CustomEvent('change')
|
|
42
|
+
* på <ix-tabs> slik at en React-wrapper kan lese aria-selected og kalle
|
|
43
|
+
* onChange. Programmatisk init og ekstern (kontrollert) synk sender IKKE
|
|
44
|
+
* change — kun ekte brukerinteraksjon.
|
|
45
|
+
* 7. En MutationObserver re-wirer når faner/paneler legges til eller fjernes
|
|
46
|
+
* (typisk React conditional rendering), og reagerer på ekstern endring av
|
|
47
|
+
* aria-selected (kontrollert React-modus) ved å reconcile-e resten.
|
|
48
|
+
*
|
|
49
|
+
* ## Ingen Shadow DOM
|
|
50
|
+
*
|
|
51
|
+
* Light DOM slik at CSS fra @sb1/indeks-css treffer .ix-tabs__* og tag-
|
|
52
|
+
* selektorene direkte, og ARIA-relasjonene fungerer på tvers av elementene.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* <ix-tabs class="ix-tabs">
|
|
56
|
+
* <ix-tab-list class="ix-tabs__list">
|
|
57
|
+
* <ix-tab class="ix-tabs__tab">Oversikt</ix-tab>
|
|
58
|
+
* <ix-tab class="ix-tabs__tab">Detaljer</ix-tab>
|
|
59
|
+
* </ix-tab-list>
|
|
60
|
+
* <ix-tab-panel class="ix-tabs__panel">Oversikt-innhold</ix-tab-panel>
|
|
61
|
+
* <ix-tab-panel class="ix-tabs__panel">Detaljer-innhold</ix-tab-panel>
|
|
62
|
+
* </ix-tabs>
|
|
63
|
+
*/
|
|
64
|
+
export declare class IxTabs extends HTMLElement {
|
|
65
|
+
private _mutationObserver;
|
|
66
|
+
private _clickListener;
|
|
67
|
+
private _keydownListener;
|
|
68
|
+
private _instanceId;
|
|
69
|
+
private _internalUpdate;
|
|
70
|
+
connectedCallback(): void;
|
|
71
|
+
disconnectedCallback(): void;
|
|
72
|
+
/** Faner i rekkefølge (direkte barn av tab-lista). */
|
|
73
|
+
private _getTabs;
|
|
74
|
+
/** Paneler i rekkefølge (direkte barn av <ix-tabs>). */
|
|
75
|
+
private _getPanels;
|
|
76
|
+
/**
|
|
77
|
+
* Finn panelet en fane styrer. Prioritet:
|
|
78
|
+
* 1. Eksplisitt `aria-controls` (forfatter- eller WC-satt).
|
|
79
|
+
* 2. Delt `data-value` — kobler fane og panel eksplisitt, uavhengig av
|
|
80
|
+
* DOM-rekkefølge (React setter dette; ren HTML kan også sette det).
|
|
81
|
+
* 3. Posisjon (fane[i] → panel[i]) som siste utvei for enkel markup.
|
|
82
|
+
*/
|
|
83
|
+
private _getPanel;
|
|
84
|
+
/**
|
|
85
|
+
* Kobler roller, IDer og kryssreferanser, og setter initial valgt fane.
|
|
86
|
+
* Idempotent — kan kjøres på nytt når DOM endres.
|
|
87
|
+
*/
|
|
88
|
+
private _wire;
|
|
89
|
+
/**
|
|
90
|
+
* Aktiverer en fane: flytter aria-selected + roving tabindex hit, viser dens
|
|
91
|
+
* panel og skjuler de øvrige. Sender kun change ved ekte brukerinteraksjon
|
|
92
|
+
* (dispatch=true) OG når valget faktisk endres.
|
|
93
|
+
*/
|
|
94
|
+
setSelected(selected: HTMLElement | null | undefined, dispatch?: boolean): void;
|
|
95
|
+
/**
|
|
96
|
+
* Vis/skjul et panel. Skjult panel fjernes fra a11y-treet og lesefokus.
|
|
97
|
+
* Panelet gjøres ikke fokuserbart — det inneholder eget innhold (tekst,
|
|
98
|
+
* lenker, felt) og skal ikke være en egen tab-stopp. Har panelet ikke
|
|
99
|
+
* fokuserbart innhold, klarer skjermlesere seg med aria-labelledby-koblingen.
|
|
100
|
+
*/
|
|
101
|
+
private _syncPanel;
|
|
102
|
+
/** Flytt fokus (roving tabindex) til en fane uten å aktivere den. */
|
|
103
|
+
private _moveFocus;
|
|
104
|
+
/** Neste fane fra `from`, i retning `step`, med loop rundt endene. */
|
|
105
|
+
private _step;
|
|
106
|
+
private _onClick;
|
|
107
|
+
private _onKeydown;
|
|
108
|
+
private _onMutations;
|
|
109
|
+
}
|
|
110
|
+
/** role="tablist" + horisontal orientering. Koordineres av <ix-tabs>. */
|
|
111
|
+
export declare class IxTabList extends HTMLElement {
|
|
112
|
+
connectedCallback(): void;
|
|
113
|
+
}
|
|
114
|
+
/** role="tab". Aktiv-tilstand og tabindex styres av <ix-tabs>. */
|
|
115
|
+
export declare class IxTab extends HTMLElement {
|
|
116
|
+
connectedCallback(): void;
|
|
117
|
+
}
|
|
118
|
+
/** role="tabpanel". Vis/skjul styres av <ix-tabs>. */
|
|
119
|
+
export declare class IxTabPanel extends HTMLElement {
|
|
120
|
+
connectedCallback(): void;
|
|
121
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plattform-deteksjon for web components.
|
|
3
|
+
*
|
|
4
|
+
* Funksjoner (ikke modul-konstanter) fordi userAgent leses ved kall — det gjør
|
|
5
|
+
* grenene testbare via stub av `navigator.userAgent`, og unngår å cache en verdi
|
|
6
|
+
* ved import-tid som kan være feil i SSR-/testmiljø.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* True når koden kjører på iOS (iPhone/iPad/iPod).
|
|
10
|
+
*
|
|
11
|
+
* Brukes til iOS-spesifikke a11y-workarounds, f.eks. at VoiceOver ikke leser
|
|
12
|
+
* løpende `aria-valuenow`-endringer på `role="progressbar"` — se IxProgressBar.
|
|
13
|
+
* SSR-guardet: returnerer false når `navigator` ikke finnes.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isIOS(): boolean;
|