@arbetsformedlingen/yrkesvaljaren 2.0.5
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/LICENSE +190 -0
- package/README.md +369 -0
- package/dist/cjs/af-validation-message-BzY5H0xU.js +16 -0
- package/dist/cjs/core-selector.cjs.entry.js +476 -0
- package/dist/cjs/digi-service-job-selector.cjs.entry.js +1619 -0
- package/dist/cjs/fuse-BTLNKxuL.js +1631 -0
- package/dist/cjs/geo-valjaren.cjs.entry.js +14910 -0
- package/dist/cjs/index-BOuTGlEp.js +1817 -0
- package/dist/cjs/index.cjs.js +14 -0
- package/dist/cjs/job-selector-demo.cjs.entry.js +692 -0
- package/dist/cjs/loader.cjs.js +12 -0
- package/dist/cjs/neobrutal-search.cjs.entry.js +171 -0
- package/dist/cjs/search.engine-D0cMelRi.js +489 -0
- package/dist/cjs/taxonomy-registry-DVmuDUpM.js +435 -0
- package/dist/cjs/types-CtHBl6Ra.js +14 -0
- package/dist/cjs/yrkesvaljaren.cjs.js +24 -0
- package/dist/collection/collection-manifest.json +15 -0
- package/dist/collection/components/core-selector/core-selector.css +803 -0
- package/dist/collection/components/core-selector/core-selector.js +761 -0
- package/dist/collection/components/core-selector/types.js +1 -0
- package/dist/collection/components/design-compat/af-icon.js +13 -0
- package/dist/collection/components/design-compat/af-label.js +2 -0
- package/dist/collection/components/design-compat/af-tag.js +6 -0
- package/dist/collection/components/design-compat/af-validation-message.js +11 -0
- package/dist/collection/components/design-compat/index.js +4 -0
- package/dist/collection/components/geovaljaren/geovaljaren.css +21 -0
- package/dist/collection/components/geovaljaren/geovaljaren.js +304 -0
- package/dist/collection/components/geovaljaren/logic/adapter.utils.js +20 -0
- package/dist/collection/components/geovaljaren/logic/data.utils.js +12 -0
- package/dist/collection/components/geovaljaren/logic/history-service.js +25 -0
- package/dist/collection/components/geovaljaren/logic/search.engine.js +28 -0
- package/dist/collection/components/job-selector/constants.js +26 -0
- package/dist/collection/components/job-selector/history-service.js +41 -0
- package/dist/collection/components/job-selector/job-selector-controller.js +835 -0
- package/dist/collection/components/job-selector/job-selector.css +318 -0
- package/dist/collection/components/job-selector/job-selector.js +1378 -0
- package/dist/collection/components/job-selector/keyboard-handlers.js +84 -0
- package/dist/collection/components/job-selector/logic/adapter.utils.js +75 -0
- package/dist/collection/components/job-selector/logic/label-formatter.js +118 -0
- package/dist/collection/components/job-selector/logic/search.engine.js +50 -0
- package/dist/collection/components/job-selector/search-logic.js +35 -0
- package/dist/collection/components/job-selector/search.utils.js +224 -0
- package/dist/collection/components/job-selector/suggestions-list.js +31 -0
- package/dist/collection/components/job-selector/taxonomy-registry.js +60 -0
- package/dist/collection/components/job-selector/test-data.mock.js +33 -0
- package/dist/collection/components/job-selector/types.js +10 -0
- package/dist/collection/components/job-selector/validation-message.js +24 -0
- package/dist/collection/demo-data.js +20 -0
- package/dist/collection/index.js +7 -0
- package/dist/collection/verkstad/verkstad.js +407 -0
- package/dist/components/core-selector.d.ts +11 -0
- package/dist/components/core-selector.js +1 -0
- package/dist/components/core-selector2.js +1 -0
- package/dist/components/digi-service-job-selector.d.ts +11 -0
- package/dist/components/digi-service-job-selector.js +1 -0
- package/dist/components/fuse.js +1 -0
- package/dist/components/geo-valjaren.d.ts +11 -0
- package/dist/components/geo-valjaren.js +1 -0
- package/dist/components/index.d.ts +35 -0
- package/dist/components/index.js +1 -0
- package/dist/components/job-selector-demo.d.ts +11 -0
- package/dist/components/job-selector-demo.js +1 -0
- package/dist/components/job-selector.js +1 -0
- package/dist/components/neobrutal-search.d.ts +11 -0
- package/dist/components/neobrutal-search.js +1 -0
- package/dist/components/search.engine.js +1 -0
- package/dist/components/types.js +1 -0
- package/dist/esm/af-validation-message-F8NIH2rC.js +14 -0
- package/dist/esm/core-selector.entry.js +474 -0
- package/dist/esm/digi-service-job-selector.entry.js +1617 -0
- package/dist/esm/fuse-BPnhMTul.js +1629 -0
- package/dist/esm/geo-valjaren.entry.js +14908 -0
- package/dist/esm/index-CGXZllj2.js +1806 -0
- package/dist/esm/index.js +10 -0
- package/dist/esm/job-selector-demo.entry.js +690 -0
- package/dist/esm/loader.js +10 -0
- package/dist/esm/neobrutal-search.entry.js +169 -0
- package/dist/esm/search.engine-nZ7AcXba.js +479 -0
- package/dist/esm/taxonomy-registry-Dw0VR9Aw.js +430 -0
- package/dist/esm/types-DHUftoZJ.js +12 -0
- package/dist/esm/yrkesvaljaren.js +20 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.js +1 -0
- package/dist/types/components/core-selector/core-selector.d.ts +46 -0
- package/dist/types/components/core-selector/types.d.ts +1 -0
- package/dist/types/components/design-compat/af-icon.d.ts +7 -0
- package/dist/types/components/design-compat/af-label.d.ts +8 -0
- package/dist/types/components/design-compat/af-tag.d.ts +8 -0
- package/dist/types/components/design-compat/af-validation-message.d.ts +9 -0
- package/dist/types/components/design-compat/index.d.ts +5 -0
- package/dist/types/components/geovaljaren/geovaljaren.d.ts +24 -0
- package/dist/types/components/geovaljaren/logic/adapter.utils.d.ts +7 -0
- package/dist/types/components/geovaljaren/logic/data.utils.d.ts +7 -0
- package/dist/types/components/geovaljaren/logic/history-service.d.ts +6 -0
- package/dist/types/components/geovaljaren/logic/search.engine.d.ts +7 -0
- package/dist/types/components/job-selector/ambient.d.ts +4 -0
- package/dist/types/components/job-selector/constants.d.ts +13 -0
- package/dist/types/components/job-selector/history-service.d.ts +6 -0
- package/dist/types/components/job-selector/job-selector-controller.d.ts +82 -0
- package/dist/types/components/job-selector/job-selector.d.ts +140 -0
- package/dist/types/components/job-selector/keyboard-handlers.d.ts +13 -0
- package/dist/types/components/job-selector/logic/adapter.utils.d.ts +12 -0
- package/dist/types/components/job-selector/logic/label-formatter.d.ts +4 -0
- package/dist/types/components/job-selector/logic/search.engine.d.ts +21 -0
- package/dist/types/components/job-selector/search-logic.d.ts +17 -0
- package/dist/types/components/job-selector/search.utils.d.ts +10 -0
- package/dist/types/components/job-selector/suggestions-list.d.ts +13 -0
- package/dist/types/components/job-selector/taxonomy-registry.d.ts +20 -0
- package/dist/types/components/job-selector/test-data.mock.d.ts +7 -0
- package/dist/types/components/job-selector/types.d.ts +107 -0
- package/dist/types/components/job-selector/validation-message.d.ts +8 -0
- package/dist/types/components/job-selector-demo/job-selector-demo.d.ts +55 -0
- package/dist/types/components/neobrutal-search/neobrutal-search.d.ts +18 -0
- package/dist/types/components.d.ts +425 -0
- package/dist/types/demo-data.d.ts +16 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/stencil-public-runtime.d.ts +1873 -0
- package/dist/types/verkstad/verkstad.d.ts +1 -0
- package/dist/yrkesvaljaren/fuse-B0eHOxow.js.map +1 -0
- package/dist/yrkesvaljaren/index-Cy4BCo5s.js.map +1 -0
- package/dist/yrkesvaljaren/index-DfJXTQqL.js.map +1 -0
- package/dist/yrkesvaljaren/index.esm.js +1 -0
- package/dist/yrkesvaljaren/index.esm.js.map +1 -0
- package/dist/yrkesvaljaren/p-16636162.entry.js +1 -0
- package/dist/yrkesvaljaren/p-273cf6df.entry.js +1 -0
- package/dist/yrkesvaljaren/p-9e48afa4.entry.js +1 -0
- package/dist/yrkesvaljaren/p-BPnhMTul.js +1 -0
- package/dist/yrkesvaljaren/p-CGXZllj2.js +3 -0
- package/dist/yrkesvaljaren/p-DHUftoZJ.js +1 -0
- package/dist/yrkesvaljaren/p-DYFAzvqX.js +1 -0
- package/dist/yrkesvaljaren/p-Dw0VR9Aw.js +1 -0
- package/dist/yrkesvaljaren/p-aef7b5ae.entry.js +1 -0
- package/dist/yrkesvaljaren/p-b0f16abd.entry.js +1 -0
- package/dist/yrkesvaljaren/p-uBrs3sSs.js +1 -0
- package/dist/yrkesvaljaren/search.engine-DPT6roRm.js.map +1 -0
- package/dist/yrkesvaljaren/taxonomy-registry-s5khf6s4.js.map +1 -0
- package/dist/yrkesvaljaren/types-DHUftoZJ.js.map +1 -0
- package/dist/yrkesvaljaren/yrkesvaljaren.css +1 -0
- package/dist/yrkesvaljaren/yrkesvaljaren.esm.js +1 -0
- package/dist/yrkesvaljaren/yrkesvaljaren.esm.js.map +1 -0
- package/loader/cdn.js +1 -0
- package/loader/index.cjs.js +1 -0
- package/loader/index.d.ts +24 -0
- package/loader/index.es2017.js +1 -0
- package/loader/index.js +2 -0
- package/package.json +126 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { h } from "@stencil/core";
|
|
2
|
+
const svgMarkup = {
|
|
3
|
+
search: '<svg width="26" height="26" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" style="width:100%;height:100%"><path d="M25.645 22.437l-5.063-5.053a1.22 1.22 0 00-.864-.355h-.827a10.477 10.477 0 002.234-6.487C21.125 4.719 16.397 0 10.562 0 4.729 0 0 4.719 0 10.542c0 5.823 4.728 10.542 10.563 10.542 2.452 0 4.707-.831 6.5-2.23v.826c0 .324.126.633.355.862l5.063 5.053c.477.476 1.25.476 1.721 0l1.437-1.435a1.22 1.22 0 00.006-1.723zm-15.082-5.408c-3.59 0-6.5-2.899-6.5-6.487a6.49 6.49 0 016.5-6.487c3.59 0 6.5 2.899 6.5 6.487a6.49 6.49 0 01-6.5 6.487z" fill="currentColor" fill-rule="nonzero"/></svg>',
|
|
4
|
+
check: '<svg width="23" height="18" viewBox="0 0 23 18" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" style="width:100%;height:100%"><path d="M20.547.18L7.315 13.627 2.453 8.686a.597.597 0 00-.853 0L.177 10.132a.62.62 0 000 .868l6.711 6.82c.236.24.618.24.854 0L22.823 2.493a.62.62 0 000-.867L21.4.18a.597.597 0 00-.853 0z" fill="currentColor"/></svg>',
|
|
5
|
+
x: '<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" style="width:100%;height:100%"><g stroke="currentColor" stroke-width="2" fill="none" fill-rule="evenodd" stroke-linecap="square"><path d="M2 2l7.986 7.986M9.986 2L2 9.986"/></g></svg>',
|
|
6
|
+
};
|
|
7
|
+
export const AfIcon = ({ name, class: className }) => (h("span", { class: className, "aria-hidden": "true", innerHTML: svgMarkup[name], style: {
|
|
8
|
+
display: 'flex',
|
|
9
|
+
width: className ? undefined : '12px',
|
|
10
|
+
height: className ? undefined : '12px',
|
|
11
|
+
minHeight: '1.4em',
|
|
12
|
+
alignItems: 'center',
|
|
13
|
+
} }));
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { h } from "@stencil/core";
|
|
2
|
+
export const AfLabel = ({ label, forId, description, required = false }) => (h("div", { class: "af-form-label" }, h("label", { class: "af-form-label__label", htmlFor: forId }, label, required ? ' *' : ''), description && h("span", { class: "af-form-label__description" }, description)));
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { h } from "@stencil/core";
|
|
2
|
+
import { AfIcon } from "./af-icon";
|
|
3
|
+
export const AfTag = ({ text, breakWords = false, onRemove }) => (h("span", { class: "af-tag" }, h("button", { type: "button", class: "af-tag__button", "aria-label": `Ta bort ${text}`, onClick: (event) => {
|
|
4
|
+
event.preventDefault();
|
|
5
|
+
onRemove();
|
|
6
|
+
} }, h("span", { class: { 'af-tag__text': true, 'af-tag__text--break': breakWords } }, text), h(AfIcon, { name: "x", class: "af-tag__remove-icon" }))));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { h } from "@stencil/core";
|
|
2
|
+
export const AfValidationMessage = (props, children) => {
|
|
3
|
+
const extraClass = props.class || '';
|
|
4
|
+
return (h("div", { id: props.id, role: props.role, class: { [extraClass]: !!extraClass } }, h("div", { class: {
|
|
5
|
+
'af-validation-message': true,
|
|
6
|
+
[`af-validation-message--${props.variant}`]: true,
|
|
7
|
+
} }, h("span", { class: {
|
|
8
|
+
'af-validation-message__icon': true,
|
|
9
|
+
[`af-validation-message__icon--${props.variant}`]: true,
|
|
10
|
+
}, "aria-hidden": "true" }), h("span", { class: "af-validation-message__text" }, children))));
|
|
11
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: block;
|
|
3
|
+
width: 100%;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.geo-valjaren-wrapper {
|
|
7
|
+
position: relative;
|
|
8
|
+
width: 100%;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.history-header {
|
|
12
|
+
user-select: none;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.history-header a {
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.history-header a:hover {
|
|
20
|
+
text-decoration: underline !important;
|
|
21
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { h } from "@stencil/core";
|
|
2
|
+
import { GeoDataService } from "./logic/data.utils";
|
|
3
|
+
import { SearchEngine } from "./logic/search.engine";
|
|
4
|
+
import { GeoHistoryService } from "./logic/history-service";
|
|
5
|
+
export class Geovaljaren {
|
|
6
|
+
label = 'Din kommun';
|
|
7
|
+
placeholder = 'Vilken kommun bor du i?';
|
|
8
|
+
selectorState = {
|
|
9
|
+
status: 'loading',
|
|
10
|
+
inputValue: '',
|
|
11
|
+
suggestions: [],
|
|
12
|
+
selectedItems: [],
|
|
13
|
+
validation: 'idle'
|
|
14
|
+
};
|
|
15
|
+
selectionEmitter;
|
|
16
|
+
config = {
|
|
17
|
+
placeholder: this.placeholder,
|
|
18
|
+
minLength: 1,
|
|
19
|
+
maxResults: 10,
|
|
20
|
+
enableMultiple: false,
|
|
21
|
+
showSearchIcon: true,
|
|
22
|
+
requireSelection: true,
|
|
23
|
+
size: 'medium',
|
|
24
|
+
enableToast: false,
|
|
25
|
+
showHistoryHeader: true
|
|
26
|
+
};
|
|
27
|
+
engine = null;
|
|
28
|
+
allGeoItems = [];
|
|
29
|
+
async componentWillLoad() {
|
|
30
|
+
this.selectorState = { ...this.selectorState, status: 'loading' };
|
|
31
|
+
try {
|
|
32
|
+
this.allGeoItems = await GeoDataService.fetchAll();
|
|
33
|
+
this.engine = new SearchEngine(this.allGeoItems);
|
|
34
|
+
this.selectorState = { ...this.selectorState, status: 'searching' };
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
console.error('Failed to load geo data', err);
|
|
38
|
+
this.selectorState = { ...this.selectorState, status: 'error', message: 'Kunde inte ladda kommuner.' };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
showHistory() {
|
|
42
|
+
if (!this.allGeoItems.length)
|
|
43
|
+
return;
|
|
44
|
+
// Don't show history if there's typed text
|
|
45
|
+
if (this.selectorState.inputValue.trim() !== '')
|
|
46
|
+
return;
|
|
47
|
+
const historyItems = GeoHistoryService.loadHistory();
|
|
48
|
+
// Lazy Validation: Only show items that still exist in our current dataset
|
|
49
|
+
const validatedHistory = historyItems.filter(hItem => this.allGeoItems.some(item => item.id === hItem.id));
|
|
50
|
+
this.selectorState = {
|
|
51
|
+
...this.selectorState,
|
|
52
|
+
status: 'searching',
|
|
53
|
+
suggestions: validatedHistory,
|
|
54
|
+
inputValue: this.selectorState.inputValue
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
addToHistory(item) {
|
|
58
|
+
const currentHistory = GeoHistoryService.loadHistory();
|
|
59
|
+
const filtered = currentHistory.filter(h => h.id !== item.id);
|
|
60
|
+
const newHistory = [item, ...filtered].slice(0, 10);
|
|
61
|
+
GeoHistoryService.saveHistory(newHistory);
|
|
62
|
+
}
|
|
63
|
+
clearHistory() {
|
|
64
|
+
GeoHistoryService.clearHistory();
|
|
65
|
+
this.selectorState = {
|
|
66
|
+
...this.selectorState,
|
|
67
|
+
suggestions: []
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
handleInput(event) {
|
|
71
|
+
const query = event.detail;
|
|
72
|
+
if (!this.engine)
|
|
73
|
+
return;
|
|
74
|
+
if (query.trim() === '') {
|
|
75
|
+
this.selectorState = { ...this.selectorState, inputValue: '' };
|
|
76
|
+
this.showHistory();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const suggestions = query.length >= this.config.minLength ? this.engine.search(query) : [];
|
|
80
|
+
const wasInError = this.selectorState.validation === 'error' || this.selectorState.validationContext === 'from-error';
|
|
81
|
+
this.selectorState = {
|
|
82
|
+
...this.selectorState,
|
|
83
|
+
inputValue: query,
|
|
84
|
+
suggestions,
|
|
85
|
+
status: 'searching',
|
|
86
|
+
validation: 'idle',
|
|
87
|
+
validationContext: wasInError ? 'from-error' : undefined
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
handleFocus() {
|
|
91
|
+
// Handled by csRequestHistory
|
|
92
|
+
}
|
|
93
|
+
handleRequestHistory() {
|
|
94
|
+
this.showHistory();
|
|
95
|
+
}
|
|
96
|
+
handleSelect(event) {
|
|
97
|
+
const selection = event.detail;
|
|
98
|
+
const wasInError = this.selectorState.validation === 'error' || this.selectorState.validationContext === 'from-error';
|
|
99
|
+
this.addToHistory(selection);
|
|
100
|
+
this.selectorState = {
|
|
101
|
+
...this.selectorState,
|
|
102
|
+
status: 'itemSelected',
|
|
103
|
+
selectedItem: selection,
|
|
104
|
+
suggestions: [],
|
|
105
|
+
inputValue: selection.label, // Keep label as input value
|
|
106
|
+
validation: wasInError ? 'success' : 'idle',
|
|
107
|
+
message: wasInError ? 'Klart!' : '',
|
|
108
|
+
validationContext: wasInError ? 'from-error' : undefined
|
|
109
|
+
};
|
|
110
|
+
this.selectionEmitter.emit(selection);
|
|
111
|
+
if (wasInError) {
|
|
112
|
+
setTimeout(() => {
|
|
113
|
+
if (this.selectorState.status === 'itemSelected' && this.selectorState.validation === 'success') {
|
|
114
|
+
this.selectorState = { ...this.selectorState, validation: 'idle' };
|
|
115
|
+
}
|
|
116
|
+
}, 3000);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
handleBlur() {
|
|
120
|
+
if (this.selectorState.status === 'searching' && this.selectorState.inputValue.trim() !== '') {
|
|
121
|
+
const suggestions = this.selectorState.suggestions;
|
|
122
|
+
if (suggestions.length === 0) {
|
|
123
|
+
this.selectorState = {
|
|
124
|
+
...this.selectorState,
|
|
125
|
+
validation: 'error',
|
|
126
|
+
message: 'Du måste välja ett alternativ från listan.'
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
handleClear() {
|
|
132
|
+
// Only clear if explicitly triggered by the X button
|
|
133
|
+
this.selectorState = {
|
|
134
|
+
...this.selectorState,
|
|
135
|
+
status: 'searching',
|
|
136
|
+
inputValue: '',
|
|
137
|
+
suggestions: [],
|
|
138
|
+
selectedItem: undefined,
|
|
139
|
+
validation: 'idle',
|
|
140
|
+
validationContext: undefined
|
|
141
|
+
};
|
|
142
|
+
this.showHistory();
|
|
143
|
+
}
|
|
144
|
+
handleCsClearHistory() {
|
|
145
|
+
this.clearHistory();
|
|
146
|
+
}
|
|
147
|
+
handleEdit() {
|
|
148
|
+
const wasInError = this.selectorState.validation === 'error' || this.selectorState.validationContext === 'from-error';
|
|
149
|
+
// When clicking to edit, keep the label of the item as search text
|
|
150
|
+
const currentLabel = this.selectorState.selectedItem?.label || '';
|
|
151
|
+
this.selectorState = {
|
|
152
|
+
...this.selectorState,
|
|
153
|
+
status: 'searching',
|
|
154
|
+
inputValue: currentLabel,
|
|
155
|
+
suggestions: [],
|
|
156
|
+
validation: 'idle',
|
|
157
|
+
validationContext: wasInError ? 'from-error' : undefined
|
|
158
|
+
};
|
|
159
|
+
// Don't show history if we have text
|
|
160
|
+
if (currentLabel === '') {
|
|
161
|
+
this.showHistory();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
render() {
|
|
165
|
+
return (h("div", { key: '5f7e4f0fb8fa815145ddd5690894ec7906dc2f1a', class: "geo-valjaren-wrapper" }, h("core-selector", { key: '73940c1ae5092f5f5f84bbd53edc6112e4374310', state: this.selectorState, config: { ...this.config, placeholder: this.placeholder }, label: this.label, labelDescription: "Ange den kommun du bor i f\u00F6r att se pendlingsalternativ" })));
|
|
166
|
+
}
|
|
167
|
+
static get is() { return "geo-valjaren"; }
|
|
168
|
+
static get encapsulation() { return "shadow"; }
|
|
169
|
+
static get originalStyleUrls() {
|
|
170
|
+
return {
|
|
171
|
+
"$": ["geovaljaren.css"]
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
static get styleUrls() {
|
|
175
|
+
return {
|
|
176
|
+
"$": ["geovaljaren.css"]
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
static get properties() {
|
|
180
|
+
return {
|
|
181
|
+
"label": {
|
|
182
|
+
"type": "string",
|
|
183
|
+
"mutable": false,
|
|
184
|
+
"complexType": {
|
|
185
|
+
"original": "string",
|
|
186
|
+
"resolved": "string",
|
|
187
|
+
"references": {}
|
|
188
|
+
},
|
|
189
|
+
"required": false,
|
|
190
|
+
"optional": false,
|
|
191
|
+
"docs": {
|
|
192
|
+
"tags": [],
|
|
193
|
+
"text": ""
|
|
194
|
+
},
|
|
195
|
+
"getter": false,
|
|
196
|
+
"setter": false,
|
|
197
|
+
"reflect": true,
|
|
198
|
+
"attribute": "af-label",
|
|
199
|
+
"defaultValue": "'Din kommun'"
|
|
200
|
+
},
|
|
201
|
+
"placeholder": {
|
|
202
|
+
"type": "string",
|
|
203
|
+
"mutable": false,
|
|
204
|
+
"complexType": {
|
|
205
|
+
"original": "string",
|
|
206
|
+
"resolved": "string",
|
|
207
|
+
"references": {}
|
|
208
|
+
},
|
|
209
|
+
"required": false,
|
|
210
|
+
"optional": false,
|
|
211
|
+
"docs": {
|
|
212
|
+
"tags": [],
|
|
213
|
+
"text": ""
|
|
214
|
+
},
|
|
215
|
+
"getter": false,
|
|
216
|
+
"setter": false,
|
|
217
|
+
"reflect": true,
|
|
218
|
+
"attribute": "af-placeholder",
|
|
219
|
+
"defaultValue": "'Vilken kommun bor du i?'"
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
static get states() {
|
|
224
|
+
return {
|
|
225
|
+
"selectorState": {}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
static get events() {
|
|
229
|
+
return [{
|
|
230
|
+
"method": "selectionEmitter",
|
|
231
|
+
"name": "afGeoSelected",
|
|
232
|
+
"bubbles": true,
|
|
233
|
+
"cancelable": true,
|
|
234
|
+
"composed": true,
|
|
235
|
+
"docs": {
|
|
236
|
+
"tags": [],
|
|
237
|
+
"text": ""
|
|
238
|
+
},
|
|
239
|
+
"complexType": {
|
|
240
|
+
"original": "GenericItem | null",
|
|
241
|
+
"resolved": "GenericItem<unknown> | null",
|
|
242
|
+
"references": {
|
|
243
|
+
"GenericItem": {
|
|
244
|
+
"location": "import",
|
|
245
|
+
"path": "../core-selector/types",
|
|
246
|
+
"id": "src/components/core-selector/types.ts::GenericItem",
|
|
247
|
+
"referenceLocation": "GenericItem"
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}];
|
|
252
|
+
}
|
|
253
|
+
static get listeners() {
|
|
254
|
+
return [{
|
|
255
|
+
"name": "csInput",
|
|
256
|
+
"method": "handleInput",
|
|
257
|
+
"target": undefined,
|
|
258
|
+
"capture": false,
|
|
259
|
+
"passive": false
|
|
260
|
+
}, {
|
|
261
|
+
"name": "csFocus",
|
|
262
|
+
"method": "handleFocus",
|
|
263
|
+
"target": undefined,
|
|
264
|
+
"capture": false,
|
|
265
|
+
"passive": false
|
|
266
|
+
}, {
|
|
267
|
+
"name": "csRequestHistory",
|
|
268
|
+
"method": "handleRequestHistory",
|
|
269
|
+
"target": undefined,
|
|
270
|
+
"capture": false,
|
|
271
|
+
"passive": false
|
|
272
|
+
}, {
|
|
273
|
+
"name": "csSelect",
|
|
274
|
+
"method": "handleSelect",
|
|
275
|
+
"target": undefined,
|
|
276
|
+
"capture": false,
|
|
277
|
+
"passive": false
|
|
278
|
+
}, {
|
|
279
|
+
"name": "csBlur",
|
|
280
|
+
"method": "handleBlur",
|
|
281
|
+
"target": undefined,
|
|
282
|
+
"capture": false,
|
|
283
|
+
"passive": false
|
|
284
|
+
}, {
|
|
285
|
+
"name": "csClear",
|
|
286
|
+
"method": "handleClear",
|
|
287
|
+
"target": undefined,
|
|
288
|
+
"capture": false,
|
|
289
|
+
"passive": false
|
|
290
|
+
}, {
|
|
291
|
+
"name": "csClearHistory",
|
|
292
|
+
"method": "handleCsClearHistory",
|
|
293
|
+
"target": undefined,
|
|
294
|
+
"capture": false,
|
|
295
|
+
"passive": false
|
|
296
|
+
}, {
|
|
297
|
+
"name": "csEditSelection",
|
|
298
|
+
"method": "handleEdit",
|
|
299
|
+
"target": undefined,
|
|
300
|
+
"capture": false,
|
|
301
|
+
"passive": false
|
|
302
|
+
}];
|
|
303
|
+
}
|
|
304
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const GeoAdapter = {
|
|
2
|
+
/**
|
|
3
|
+
* Transforms the pendlingskommuner JSON data structure into GenericItems.
|
|
4
|
+
*/
|
|
5
|
+
toGenericItems: (jsonData) => {
|
|
6
|
+
if (!jsonData || !jsonData.data) {
|
|
7
|
+
console.warn("Invalid JSON data structure for GeoAdapter");
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
// jsonData.data is an object where keys are IDs
|
|
11
|
+
return Object.entries(jsonData.data).map(([id, value]) => {
|
|
12
|
+
return {
|
|
13
|
+
id: id,
|
|
14
|
+
label: value.preferred_label || 'Okänd kommun',
|
|
15
|
+
description: `Kommun-ID: ${id}`, // We don't have much else to show right now
|
|
16
|
+
metadata: value // Keep original for destinations access later if needed
|
|
17
|
+
};
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import geoData from "../assets/pendlingskommuner.json";
|
|
2
|
+
import { GeoAdapter } from "./adapter.utils";
|
|
3
|
+
export const GeoDataService = {
|
|
4
|
+
/**
|
|
5
|
+
* Simulates an async fetch of geo data
|
|
6
|
+
*/
|
|
7
|
+
fetchAll: async () => {
|
|
8
|
+
// Simulate network delay
|
|
9
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
10
|
+
return GeoAdapter.toGenericItems(geoData);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const GeoHistoryService = {
|
|
2
|
+
saveHistory(history) {
|
|
3
|
+
try {
|
|
4
|
+
localStorage.setItem('geovaljaren_history', JSON.stringify(history));
|
|
5
|
+
}
|
|
6
|
+
catch (e) {
|
|
7
|
+
console.error('[GeoHistoryService] Kunde inte spara historik.', e);
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
loadHistory() {
|
|
11
|
+
try {
|
|
12
|
+
const historyString = localStorage.getItem('geovaljaren_history');
|
|
13
|
+
if (historyString) {
|
|
14
|
+
return JSON.parse(historyString);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
console.error('[GeoHistoryService] Kunde inte läsa historik.', e);
|
|
19
|
+
}
|
|
20
|
+
return [];
|
|
21
|
+
},
|
|
22
|
+
clearHistory() {
|
|
23
|
+
localStorage.removeItem('geovaljaren_history');
|
|
24
|
+
}
|
|
25
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import Fuse from "fuse.js";
|
|
2
|
+
const FUSE_OPTIONS = {
|
|
3
|
+
includeScore: true,
|
|
4
|
+
threshold: 0.3,
|
|
5
|
+
keys: [
|
|
6
|
+
{ name: 'label', weight: 2 },
|
|
7
|
+
{ name: 'description', weight: 1 }
|
|
8
|
+
],
|
|
9
|
+
shouldSort: true,
|
|
10
|
+
minMatchCharLength: 1,
|
|
11
|
+
};
|
|
12
|
+
export class SearchEngine {
|
|
13
|
+
fuse = null;
|
|
14
|
+
constructor(data) {
|
|
15
|
+
this.updateIndex(data);
|
|
16
|
+
}
|
|
17
|
+
search(query, maxResults = 10) {
|
|
18
|
+
if (!this.fuse || !query)
|
|
19
|
+
return [];
|
|
20
|
+
// Fuse search
|
|
21
|
+
const results = this.fuse.search(query);
|
|
22
|
+
// Map back to item
|
|
23
|
+
return results.map(r => r.item).slice(0, maxResults);
|
|
24
|
+
}
|
|
25
|
+
updateIndex(newData) {
|
|
26
|
+
this.fuse = new Fuse(newData, FUSE_OPTIONS);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Minimum number of characters required to trigger search
|
|
2
|
+
export const MIN_INPUT_LENGTH = 1;
|
|
3
|
+
// Debounce time for search input (ms)
|
|
4
|
+
export const DEBOUNCE_TIME = 100; // ms - Återställt till 250 baserat på sok-box.tsx initialt
|
|
5
|
+
// Default maximum number of results to display
|
|
6
|
+
export const MAX_RESULTS = 10;
|
|
7
|
+
// Configuration for Fuse.js
|
|
8
|
+
export const FUSE_OPTIONS = {
|
|
9
|
+
keys: ['preferred_label'], // Ändrat från 'label' till 'preferred_label' för att matcha datastrukturen
|
|
10
|
+
includeScore: true,
|
|
11
|
+
ignoreDiacritics: true, // Lägg till för att matcha t.ex. vard -> vård
|
|
12
|
+
ignoreFieldNorm: true, // Lägg till för att ignorera fältlängd vid poängsättning
|
|
13
|
+
threshold: 0.6, // Behåll 0.6
|
|
14
|
+
minMatchCharLength: MIN_INPUT_LENGTH, // Refererar till konstant ovan
|
|
15
|
+
ignoreLocation: true, // Match anywhere in the string
|
|
16
|
+
distance: 100, // Allow matches further apart
|
|
17
|
+
};
|
|
18
|
+
// UI Text Strings
|
|
19
|
+
export const PLACEHOLDER_TEXT = "T.ex. frisör, vårdbiträde, snickare"; // Uppdaterad standardplaceholder enligt Figma-design
|
|
20
|
+
export const NO_RESULTS_TEXT = "Inga resultat matchade din sökning.";
|
|
21
|
+
export const VALIDATION_ERROR_REQUIRED_FIELD = "Du måste göra ett val";
|
|
22
|
+
export const VALIDATION_ERROR_EMPTY_FIELD = "Välj ett yrke från listan";
|
|
23
|
+
// Nya konstanter för aria-label
|
|
24
|
+
export const CLEAR_BUTTON_LABEL_TEXT = "Rensa sökfält";
|
|
25
|
+
export const REMOVE_ITEM_LABEL_TEXT_PREFIX = "Ta bort";
|
|
26
|
+
export const POSITIVE_VALIDATION_TEXT = "Valt yrke";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import log from "loglevel";
|
|
2
|
+
let memoryCache = null;
|
|
3
|
+
export const HistoryService = {
|
|
4
|
+
saveHistory(history) {
|
|
5
|
+
memoryCache = history; // Omedelbar uppdatering av UI-state
|
|
6
|
+
try {
|
|
7
|
+
localStorage.setItem('job-selector_history', JSON.stringify(history));
|
|
8
|
+
log.debug('[HistoryService] saveHistory - History saved.');
|
|
9
|
+
}
|
|
10
|
+
catch (e) {
|
|
11
|
+
log.error('Kunde inte spara historik.', e);
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
loadHistory() {
|
|
15
|
+
if (memoryCache) {
|
|
16
|
+
return memoryCache;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const historyString = localStorage.getItem('job-selector_history');
|
|
20
|
+
if (historyString) {
|
|
21
|
+
memoryCache = JSON.parse(historyString);
|
|
22
|
+
return memoryCache;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
log.error('Kunde inte läsa historik.', e);
|
|
27
|
+
}
|
|
28
|
+
memoryCache = [];
|
|
29
|
+
return memoryCache;
|
|
30
|
+
},
|
|
31
|
+
clearHistory() {
|
|
32
|
+
memoryCache = [];
|
|
33
|
+
try {
|
|
34
|
+
localStorage.removeItem('job-selector_history');
|
|
35
|
+
log.debug('[HistoryService] clearHistory - History cleared.');
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
log.error('Kunde inte rensa historik.', e);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|