fds-vue-core 4.8.0 → 4.9.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/dist/fds-vue-core.cjs.js +114 -28
- package/dist/fds-vue-core.cjs.js.map +1 -1
- package/dist/fds-vue-core.css +1 -1
- package/dist/fds-vue-core.es.js +114 -28
- package/dist/fds-vue-core.es.js.map +1 -1
- package/package.json +2 -1
- package/src/components/FdsSearchSelectPro/FdsSearchSelectPro.stories.ts +52 -2
- package/src/components/FdsSearchSelectPro/FdsSearchSelectPro.vue +52 -7
- package/src/components/FdsSearchSelectPro/types.ts +67 -0
- package/src/components/FdsSearchSelectPro/useSearchSelectProItems.ts +65 -3
package/dist/fds-vue-core.cjs.js
CHANGED
|
@@ -1015,7 +1015,7 @@ const _hoisted_10$3 = { class: "flex items-start justify-between gap-4" };
|
|
|
1015
1015
|
const _hoisted_11$3 = { class: "flex items-center gap-3" };
|
|
1016
1016
|
const _hoisted_12$2 = { class: "m-0 text-base font-main font-bold tracking-wide" };
|
|
1017
1017
|
const _hoisted_13$2 = { class: "flex items-start gap-3" };
|
|
1018
|
-
const _hoisted_14$
|
|
1018
|
+
const _hoisted_14$2 = { class: "mb-0-last-child" };
|
|
1019
1019
|
const smallIconSize = 24;
|
|
1020
1020
|
const largeIconSize = 48;
|
|
1021
1021
|
const _sfc_main$z = /* @__PURE__ */ vue.defineComponent({
|
|
@@ -1089,7 +1089,7 @@ const _sfc_main$z = /* @__PURE__ */ vue.defineComponent({
|
|
|
1089
1089
|
])
|
|
1090
1090
|
])
|
|
1091
1091
|
], 2)) : vue.createCommentVNode("", true),
|
|
1092
|
-
vue.createElementVNode("div", _hoisted_14$
|
|
1092
|
+
vue.createElementVNode("div", _hoisted_14$2, [
|
|
1093
1093
|
vue.renderSlot(_ctx.$slots, "default")
|
|
1094
1094
|
])
|
|
1095
1095
|
]))
|
|
@@ -8928,7 +8928,7 @@ const _hoisted_13$1 = {
|
|
|
8928
8928
|
key: 1,
|
|
8929
8929
|
class: "max-h-72 overflow-auto border border-gray-200 rounded-md"
|
|
8930
8930
|
};
|
|
8931
|
-
const _hoisted_14 = { class: "w-full text-left text-xs border-collapse" };
|
|
8931
|
+
const _hoisted_14$1 = { class: "w-full text-left text-xs border-collapse" };
|
|
8932
8932
|
const _hoisted_15 = ["onClick"];
|
|
8933
8933
|
const _hoisted_16 = { class: "py-1.5 px-3 font-mono break-all" };
|
|
8934
8934
|
const _hoisted_17 = { class: "py-1.5 px-3 font-mono whitespace-pre-wrap break-all" };
|
|
@@ -9270,7 +9270,7 @@ const _sfc_main$m = /* @__PURE__ */ vue.defineComponent({
|
|
|
9270
9270
|
}, {
|
|
9271
9271
|
default: vue.withCtx(() => [
|
|
9272
9272
|
!sessionStorageEntries.value.length ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_12$1, "Inga värden i sessionStorage.")) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_13$1, [
|
|
9273
|
-
vue.createElementVNode("table", _hoisted_14, [
|
|
9273
|
+
vue.createElementVNode("table", _hoisted_14$1, [
|
|
9274
9274
|
_cache[15] || (_cache[15] = vue.createElementVNode("thead", { class: "bg-gray-50" }, [
|
|
9275
9275
|
vue.createElementVNode("tr", null, [
|
|
9276
9276
|
vue.createElementVNode("th", { class: "py-2 px-3 font-semibold" }, "Nyckel"),
|
|
@@ -10454,7 +10454,6 @@ const useSearchSelectProItems = ({
|
|
|
10454
10454
|
});
|
|
10455
10455
|
};
|
|
10456
10456
|
const matchesSearchTerm = (item) => {
|
|
10457
|
-
if (isDividerItem(item) && searchTerm.value) return false;
|
|
10458
10457
|
if (!searchTerm.value) return true;
|
|
10459
10458
|
const searchValue = isPid.value ? searchTerm.value.replace(/\D/g, "") : searchTerm.value;
|
|
10460
10459
|
const searchLower = searchValue.toLowerCase();
|
|
@@ -10469,6 +10468,53 @@ const useSearchSelectProItems = ({
|
|
|
10469
10468
|
return valueLower.includes(searchLower) || unmaskedValue.length > 0 && unmaskedValue.includes(searchValue);
|
|
10470
10469
|
});
|
|
10471
10470
|
};
|
|
10471
|
+
const matchesDividerSearchTerm = (item) => {
|
|
10472
|
+
if (!isDividerItem(item) || !searchTerm.value) return false;
|
|
10473
|
+
const searchValue = isPid.value ? searchTerm.value.replace(/\D/g, "") : searchTerm.value;
|
|
10474
|
+
const searchLower = searchValue.toLowerCase();
|
|
10475
|
+
const dividerSearchFields = [...searchFields.value, "label", "name"];
|
|
10476
|
+
return dividerSearchFields.some((field) => {
|
|
10477
|
+
const value = item[field];
|
|
10478
|
+
if (!value) return false;
|
|
10479
|
+
return String(value).toLowerCase().includes(searchLower);
|
|
10480
|
+
});
|
|
10481
|
+
};
|
|
10482
|
+
const filterWithDividerGroups = (sourceData) => {
|
|
10483
|
+
if (!searchTerm.value) return sourceData;
|
|
10484
|
+
const matched = [];
|
|
10485
|
+
let currentDivider = null;
|
|
10486
|
+
let currentChildren = [];
|
|
10487
|
+
const flushCurrentGroup = () => {
|
|
10488
|
+
if (!currentDivider) {
|
|
10489
|
+
matched.push(...currentChildren.filter((item) => matchesSearchTerm(item)));
|
|
10490
|
+
} else {
|
|
10491
|
+
const dividerMatched = matchesDividerSearchTerm(currentDivider);
|
|
10492
|
+
const matchedChildren = currentChildren.filter((item) => matchesSearchTerm(item));
|
|
10493
|
+
if (!dividerMatched && matchedChildren.length === 0) {
|
|
10494
|
+
return;
|
|
10495
|
+
}
|
|
10496
|
+
if (dividerMatched || props.showOutOfBoundsDivider !== false) {
|
|
10497
|
+
matched.push(currentDivider);
|
|
10498
|
+
}
|
|
10499
|
+
if (dividerMatched) {
|
|
10500
|
+
matched.push(...currentChildren);
|
|
10501
|
+
} else {
|
|
10502
|
+
matched.push(...matchedChildren);
|
|
10503
|
+
}
|
|
10504
|
+
}
|
|
10505
|
+
};
|
|
10506
|
+
sourceData.forEach((item) => {
|
|
10507
|
+
if (isDividerItem(item)) {
|
|
10508
|
+
flushCurrentGroup();
|
|
10509
|
+
currentDivider = item;
|
|
10510
|
+
currentChildren = [];
|
|
10511
|
+
return;
|
|
10512
|
+
}
|
|
10513
|
+
currentChildren.push(item);
|
|
10514
|
+
});
|
|
10515
|
+
flushCurrentGroup();
|
|
10516
|
+
return matched;
|
|
10517
|
+
};
|
|
10472
10518
|
const filterAndPaginate = (onTotal) => {
|
|
10473
10519
|
if (!sourceItems.value.length) {
|
|
10474
10520
|
matchingItems.value = [];
|
|
@@ -10480,7 +10526,7 @@ const useSearchSelectProItems = ({
|
|
|
10480
10526
|
if (props.preserveOrder && sourceData.length) {
|
|
10481
10527
|
sourceData = sortResponse(sourceData);
|
|
10482
10528
|
}
|
|
10483
|
-
const matchedArray = sourceData.filter((item) => matchesSearchTerm(item));
|
|
10529
|
+
const matchedArray = hasDividerMode.value ? filterWithDividerGroups(sourceData) : sourceData.filter((item) => matchesSearchTerm(item));
|
|
10484
10530
|
matchingItems.value = matchedArray;
|
|
10485
10531
|
const shouldLimitItems = props.maxItems !== void 0 && props.maxItems > 0;
|
|
10486
10532
|
const effectiveLimit = shouldLimitItems ? visibleItemsLimit.value ?? props.maxItems : null;
|
|
@@ -10501,7 +10547,11 @@ const useSearchSelectProItems = ({
|
|
|
10501
10547
|
selectedItems.value = selectedItems.value.filter((item) => currentIds.has(getItemIdentifier(item)));
|
|
10502
10548
|
};
|
|
10503
10549
|
const hasInternalMoreItems = vue.computed(() => matchingItems.value.length > displayedItems.value.length);
|
|
10504
|
-
const shouldShowLoadMore = vue.computed(() =>
|
|
10550
|
+
const shouldShowLoadMore = vue.computed(() => {
|
|
10551
|
+
if (hasInternalMoreItems.value) return true;
|
|
10552
|
+
if (props.maxItems && props.maxItems > 0) return false;
|
|
10553
|
+
return props.showLoadMore;
|
|
10554
|
+
});
|
|
10505
10555
|
const handleInternalLoadMore = () => {
|
|
10506
10556
|
if (props.maxItems && props.maxItems > 0 && hasInternalMoreItems.value) {
|
|
10507
10557
|
visibleItemsLimit.value = (visibleItemsLimit.value ?? props.maxItems) + props.maxItems;
|
|
@@ -10536,18 +10586,19 @@ const _hoisted_5$5 = {
|
|
|
10536
10586
|
};
|
|
10537
10587
|
const _hoisted_6$3 = ["checked"];
|
|
10538
10588
|
const _hoisted_7$3 = ["id", "role", "onMouseup"];
|
|
10539
|
-
const _hoisted_8$2 = ["
|
|
10540
|
-
const _hoisted_9$2 = ["
|
|
10541
|
-
const _hoisted_10 =
|
|
10589
|
+
const _hoisted_8$2 = ["innerHTML"];
|
|
10590
|
+
const _hoisted_9$2 = ["type", "id", "name", "checked", "onChange", "onKeydown"];
|
|
10591
|
+
const _hoisted_10 = ["innerHTML"];
|
|
10592
|
+
const _hoisted_11 = {
|
|
10542
10593
|
key: 0,
|
|
10543
10594
|
class: "border-b border-blue_t-200 p-3"
|
|
10544
10595
|
};
|
|
10545
|
-
const
|
|
10546
|
-
const
|
|
10596
|
+
const _hoisted_12 = ["aria-disabled"];
|
|
10597
|
+
const _hoisted_13 = {
|
|
10547
10598
|
key: 2,
|
|
10548
10599
|
class: "block m-0 list-none p-0"
|
|
10549
10600
|
};
|
|
10550
|
-
const
|
|
10601
|
+
const _hoisted_14 = { class: "p-3" };
|
|
10551
10602
|
const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
|
|
10552
10603
|
__name: "FdsSearchSelectPro",
|
|
10553
10604
|
props: {
|
|
@@ -10564,6 +10615,10 @@ const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
|
|
|
10564
10615
|
selectedToggleLabel: { default: "Dina val" },
|
|
10565
10616
|
showLoadMore: { type: Boolean, default: false },
|
|
10566
10617
|
loadingMore: { type: Boolean, default: false },
|
|
10618
|
+
autoLoadOnScroll: { type: Boolean, default: false },
|
|
10619
|
+
autoLoadThreshold: { default: 24 },
|
|
10620
|
+
highlightDividerMatch: { type: Boolean, default: true },
|
|
10621
|
+
showOutOfBoundsDivider: { type: Boolean, default: true },
|
|
10567
10622
|
maxItems: { default: void 0 },
|
|
10568
10623
|
page: { default: void 0 },
|
|
10569
10624
|
totalPages: { default: void 0 },
|
|
@@ -10572,6 +10627,7 @@ const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
|
|
|
10572
10627
|
searchFields: { default: () => [] },
|
|
10573
10628
|
preserveOrder: { type: Boolean, default: false },
|
|
10574
10629
|
initialValue: { default: "" },
|
|
10630
|
+
displayValue: { default: "" },
|
|
10575
10631
|
disabled: { type: Boolean, default: false },
|
|
10576
10632
|
dropdownAbsolute: { type: Boolean, default: false },
|
|
10577
10633
|
labelLeft: { type: Boolean, default: false },
|
|
@@ -10697,6 +10753,9 @@ const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
|
|
|
10697
10753
|
const resolvedLoadMoreLabel = vue.computed(() => props.locale === "en" ? "Show more" : "Visa fler");
|
|
10698
10754
|
const resolvedLoadingMoreLabel = vue.computed(() => props.locale === "en" ? "Loading more..." : "Hämtar fler...");
|
|
10699
10755
|
const displayInputValue = vue.computed(() => {
|
|
10756
|
+
if (!inputHasFocus.value && !hasSelection.value && !searchTerm.value.length && props.displayValue) {
|
|
10757
|
+
return props.displayValue;
|
|
10758
|
+
}
|
|
10700
10759
|
if (isMultiple.value && !inputHasFocus.value && selectedItems.value.length > 0) {
|
|
10701
10760
|
return `${selectedItems.value.length} val`;
|
|
10702
10761
|
}
|
|
@@ -10841,24 +10900,39 @@ const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
|
|
|
10841
10900
|
const handleMatchingString = (item) => {
|
|
10842
10901
|
if (isDividerItem(item)) {
|
|
10843
10902
|
const dividerLabel = searchFields.value.map((key) => String(item[key] ?? "").trim()).find((value) => value.length > 0) ?? String(item.label ?? item.name ?? "");
|
|
10903
|
+
if (props.highlightDividerMatch && searchTerm.value) {
|
|
10904
|
+
const formattedTerm = formatPidWithDash(searchTerm.value);
|
|
10905
|
+
const escaped = formattedTerm.replace(/[()]/g, "\\$&");
|
|
10906
|
+
if (new RegExp(escaped, "i").test(dividerLabel)) {
|
|
10907
|
+
return boldQuery(dividerLabel);
|
|
10908
|
+
}
|
|
10909
|
+
}
|
|
10844
10910
|
return dividerLabel;
|
|
10845
10911
|
}
|
|
10846
|
-
const values = searchFields.value.map((key) => String(item[key]
|
|
10912
|
+
const values = searchFields.value.map((key) => String(item[key] ?? "").trim()).filter((value) => value.length > 0);
|
|
10847
10913
|
let result = "";
|
|
10914
|
+
if (values.length === 0) {
|
|
10915
|
+
return "";
|
|
10916
|
+
}
|
|
10848
10917
|
if (values.length === 1) {
|
|
10849
|
-
result = values[0]
|
|
10918
|
+
result = values[0] ?? "";
|
|
10850
10919
|
} else {
|
|
10851
10920
|
if (values[1]?.length === 12 && parseInt(values[1])) {
|
|
10852
10921
|
values[1] = formatPidWithDash(values[1]);
|
|
10853
10922
|
}
|
|
10854
10923
|
if (props.preserveOrder) {
|
|
10855
|
-
const
|
|
10924
|
+
const primaryValue = values[0] ?? "";
|
|
10925
|
+
const secondaryValue = values[1];
|
|
10926
|
+
const combined = secondaryValue ? `${primaryValue} (${secondaryValue})` : primaryValue;
|
|
10856
10927
|
const rest = values.slice(2).join("<br>");
|
|
10857
10928
|
result = rest ? `${combined}<br>${rest}` : combined;
|
|
10858
10929
|
} else {
|
|
10859
|
-
result = values[0]
|
|
10930
|
+
result = values[0] ?? "";
|
|
10860
10931
|
for (let i = 1; i < values.length; i++) {
|
|
10861
|
-
|
|
10932
|
+
const value = values[i];
|
|
10933
|
+
if (value) {
|
|
10934
|
+
result += ` (${value})`;
|
|
10935
|
+
}
|
|
10862
10936
|
}
|
|
10863
10937
|
}
|
|
10864
10938
|
}
|
|
@@ -10962,6 +11036,16 @@ const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
|
|
|
10962
11036
|
}
|
|
10963
11037
|
emit("loadMore");
|
|
10964
11038
|
};
|
|
11039
|
+
const handleDropdownScroll = (event) => {
|
|
11040
|
+
if (!props.autoLoadOnScroll || props.loadingMore || !shouldShowLoadMore.value) return;
|
|
11041
|
+
const target = event.target;
|
|
11042
|
+
if (!target) return;
|
|
11043
|
+
const threshold = Math.max(0, props.autoLoadThreshold ?? 24);
|
|
11044
|
+
const remaining = target.scrollHeight - (target.scrollTop + target.clientHeight);
|
|
11045
|
+
if (remaining <= threshold) {
|
|
11046
|
+
handleLoadMore();
|
|
11047
|
+
}
|
|
11048
|
+
};
|
|
10965
11049
|
const handleInputFocus = () => {
|
|
10966
11050
|
inputHasFocus.value = true;
|
|
10967
11051
|
if ((isMultiple.value || !selectedItem.value) && displayedItems.value.length > 0) {
|
|
@@ -11099,7 +11183,8 @@ const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
|
|
|
11099
11183
|
ref_key: "dropdownRef",
|
|
11100
11184
|
ref: dropdownRef,
|
|
11101
11185
|
class: vue.normalizeClass(listWrapperClasses.value),
|
|
11102
|
-
style: vue.normalizeStyle(listWrapperStyle.value)
|
|
11186
|
+
style: vue.normalizeStyle(listWrapperStyle.value),
|
|
11187
|
+
onScrollPassive: handleDropdownScroll
|
|
11103
11188
|
}, [
|
|
11104
11189
|
__props.loading ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_4$5, [
|
|
11105
11190
|
vue.createVNode(_sfc_main$x, {
|
|
@@ -11141,8 +11226,9 @@ const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
|
|
|
11141
11226
|
labelClasses.value,
|
|
11142
11227
|
"font-normal text-gray-700 border-b border-gray-100 cursor-default pointer-events-none"
|
|
11143
11228
|
]),
|
|
11144
|
-
style: vue.normalizeStyle(vue.unref(optionPaddingStyle)(item))
|
|
11145
|
-
|
|
11229
|
+
style: vue.normalizeStyle(vue.unref(optionPaddingStyle)(item)),
|
|
11230
|
+
innerHTML: handleMatchingString(item)
|
|
11231
|
+
}, null, 14, _hoisted_8$2)) : (vue.openBlock(), vue.createElementBlock("label", {
|
|
11146
11232
|
key: 1,
|
|
11147
11233
|
class: vue.normalizeClass([labelClasses.value, listItemClasses.value, "flex items-start gap-2"]),
|
|
11148
11234
|
style: vue.normalizeStyle(vue.unref(optionPaddingStyle)(item))
|
|
@@ -11158,15 +11244,15 @@ const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
|
|
|
11158
11244
|
vue.withKeys((e) => !__props.multiple && handleClick(e, item), ["enter"]),
|
|
11159
11245
|
vue.withKeys(vue.withModifiers((e) => !__props.multiple && handleClick(e, item), ["prevent"]), ["space"])
|
|
11160
11246
|
]
|
|
11161
|
-
}, null, 42,
|
|
11247
|
+
}, null, 42, _hoisted_9$2),
|
|
11162
11248
|
vue.createElementVNode("span", {
|
|
11163
11249
|
class: "min-w-0 flex-1",
|
|
11164
11250
|
innerHTML: handleMatchingString(item)
|
|
11165
|
-
}, null, 8,
|
|
11251
|
+
}, null, 8, _hoisted_10)
|
|
11166
11252
|
], 6))
|
|
11167
11253
|
], 42, _hoisted_7$3);
|
|
11168
11254
|
}), 128)),
|
|
11169
|
-
vue.unref(shouldShowLoadMore) ? (vue.openBlock(), vue.createElementBlock("li",
|
|
11255
|
+
vue.unref(shouldShowLoadMore) ? (vue.openBlock(), vue.createElementBlock("li", _hoisted_11, [
|
|
11170
11256
|
vue.createElementVNode("div", {
|
|
11171
11257
|
class: vue.normalizeClass(["flex items-center gap-2 text-blue-600", { "cursor-pointer": !__props.loadingMore, "cursor-not-allowed opacity-70": __props.loadingMore }]),
|
|
11172
11258
|
role: "button",
|
|
@@ -11186,7 +11272,7 @@ const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
|
|
|
11186
11272
|
class: "w-6 h-6 pointer-events-none"
|
|
11187
11273
|
}, null, 8, ["loading", "aria-label"]),
|
|
11188
11274
|
vue.createElementVNode("span", null, vue.toDisplayString(__props.loadingMore ? resolvedLoadingMoreLabel.value : resolvedLoadMoreLabel.value), 1)
|
|
11189
|
-
], 42,
|
|
11275
|
+
], 42, _hoisted_12)
|
|
11190
11276
|
])) : vue.createCommentVNode("", true)
|
|
11191
11277
|
], 32),
|
|
11192
11278
|
__props.page !== void 0 && totalPages.value !== null && totalPages.value > 1 ? (vue.openBlock(), vue.createBlock(_sfc_main$k, {
|
|
@@ -11196,10 +11282,10 @@ const _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
|
|
|
11196
11282
|
onPaginate: handlePagination,
|
|
11197
11283
|
class: "my-4! px-2"
|
|
11198
11284
|
}, null, 8, ["current", "max"])) : vue.createCommentVNode("", true)
|
|
11199
|
-
], 64)) : !__props.loading ? (vue.openBlock(), vue.createElementBlock("ul",
|
|
11200
|
-
vue.createElementVNode("li",
|
|
11285
|
+
], 64)) : !__props.loading ? (vue.openBlock(), vue.createElementBlock("ul", _hoisted_13, [
|
|
11286
|
+
vue.createElementVNode("li", _hoisted_14, vue.toDisplayString(__props.noResultPrompt), 1)
|
|
11201
11287
|
])) : vue.createCommentVNode("", true)
|
|
11202
|
-
],
|
|
11288
|
+
], 38)) : vue.createCommentVNode("", true)
|
|
11203
11289
|
])
|
|
11204
11290
|
], 16);
|
|
11205
11291
|
};
|