@kws3/ui 1.9.1 → 2.0.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.mdx +76 -48
- package/buttons/ConfirmButton.svelte +11 -3
- package/buttons/DeleteButton.svelte +2 -3
- package/buttons/ProcessButton.svelte +3 -4
- package/buttons/SubmitButton.svelte +11 -3
- package/charts/AreaChart.svelte +1 -1
- package/charts/BarChart.svelte +1 -1
- package/charts/Chart.svelte +1 -0
- package/charts/DonutChart.svelte +1 -1
- package/charts/LineChart.svelte +1 -1
- package/charts/MixedChart.svelte +1 -1
- package/charts/PieChart.svelte +1 -1
- package/charts/RadialChart.svelte +1 -1
- package/charts/utils.js +1 -0
- package/controls/Checkbox.svelte +10 -4
- package/controls/FileUpload.svelte +14 -8
- package/controls/NumberInput.svelte +19 -10
- package/controls/Radio.svelte +8 -2
- package/controls/RangeSlider.svelte +8 -2
- package/controls/Toggle.svelte +9 -2
- package/controls/ToggleButtons.svelte +10 -3
- package/datagrid/DataSearch/DataSearch.svelte +1 -1
- package/datagrid/GridView/GridCell.svelte +3 -0
- package/datagrid/GridView/GridRow.svelte +15 -0
- package/datagrid/GridView/GridView.svelte +21 -3
- package/datagrid/Pagination/Pagination.svelte +3 -3
- package/datagrid/TileView/TileView.svelte +46 -5
- package/datagrid/TileView/TileViewItem.svelte +4 -0
- package/form/index.js +160 -0
- package/forms/AutoComplete.svelte +126 -76
- package/forms/Datepicker.svelte +22 -5
- package/forms/PasswordValidator/PasswordValidator.svelte +8 -8
- package/forms/PasswordValidator/validatePassword.js +13 -2
- package/forms/SearchInput.svelte +180 -0
- package/forms/Timepicker.svelte +69 -4
- package/forms/actions.js +21 -15
- package/forms/colorpicker/Colorpicker.js +28 -3
- package/forms/colorpicker/Colorpicker.svelte +2 -2
- package/forms/select/MultiSelect.svelte +103 -46
- package/forms/select/SearchableSelect.svelte +6 -5
- package/helpers/CardModal.svelte +2 -1
- package/helpers/ClipboardCopier.svelte +4 -1
- package/helpers/Dialog/Dialog.svelte +13 -8
- package/helpers/Dialog/index.js +6 -0
- package/helpers/Divider.svelte +2 -2
- package/helpers/FloatingUI/Floatie.svelte +6 -6
- package/helpers/FloatingUI/index.js +2 -1
- package/helpers/Icon.svelte +25 -9
- package/helpers/Loader.svelte +10 -3
- package/helpers/Message.svelte +2 -2
- package/helpers/Modal.svelte +2 -1
- package/helpers/Notification.svelte +1 -1
- package/helpers/Popover.svelte +4 -4
- package/helpers/ScrollableList.svelte +12 -8
- package/helpers/Skeleton.svelte +4 -1
- package/helpers/Timeline/Timeline.svelte +1 -1
- package/helpers/Timeline/TimelineItem.svelte +5 -5
- package/helpers/Tooltip.js +1 -1
- package/index.js +10 -4
- package/internal/fuzzy.js +116 -0
- package/internal/index.js +27 -0
- package/internal/scrollIntoActiveElement.js +22 -0
- package/keyboard/index.js +94 -0
- package/package.json +6 -4
- package/{utils/resizeObserver.js → resizeObserver/index.js} +0 -0
- package/search/index.js +52 -0
- package/settings.js +1 -1
- package/sliding-panes/SlidingPane.svelte +1 -4
- package/styles/AutoComplete.scss +2 -1
- package/styles/Datepicker.scss +1 -1
- package/styles/Grid.scss +14 -0
- package/styles/Select.scss +2 -1
- package/transitions/components/Scale.svelte +1 -0
- package/transitions/components/getEasing.js +18 -5
- package/types/ambient.d.ts +16 -0
- package/types/index.d.ts +46 -0
- package/types/type-defs/index.ts +14 -0
- package/utils/index.js +110 -9
- package/utils/fuzzysearch.js +0 -20
- package/utils/keyboard-events.js +0 -32
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
@component
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
@param {string} [value=
|
|
5
|
+
@param {?string} [value=] - Value of the Input
|
|
6
6
|
|
|
7
|
-
This property can be bound to, to fetch the current value, Default:
|
|
7
|
+
This property can be bound to, to fetch the current value, Default: ``
|
|
8
8
|
@param {string} [placeholder=""] - Placeholder text for the input, Default: `""`
|
|
9
9
|
@param {array} [options=[]] - Array of strings, or objects.
|
|
10
10
|
Used to populate the list of options in the dropdown, Default: `[]`
|
|
@@ -12,12 +12,12 @@ Used to populate the list of options in the dropdown, Default: `[]`
|
|
|
12
12
|
|
|
13
13
|
Only send this prop if you want to fetch `options` asynchronously.
|
|
14
14
|
`options` prop will be ignored if this prop is set., Default: `null`
|
|
15
|
-
@param {'fuzzy'|'strict'} [search_strategy="fuzzy"] - Filtered options to be displayed strictly based on search text or perform a fuzzy match.
|
|
15
|
+
@param {string|'fuzzy'|'strict'} [search_strategy="fuzzy"] - Filtered options to be displayed strictly based on search text or perform a fuzzy match.
|
|
16
16
|
Fuzzy match will not work if `search` function is set, as the backend service is meant to do the matching., Default: `"fuzzy"`
|
|
17
17
|
@param {boolean} [highlighted_results=true] - Whether to show the highlighted or plain results in the dropdown., Default: `true`
|
|
18
18
|
@param {number} [scoreThreshold=5] - Score threshold for fuzzy search strategy, setting high score gives more fuzzy matches., Default: `5`
|
|
19
|
-
@param {''|'small'|'medium'|'large'} [size=""] - Size of the input, Default: `""`
|
|
20
|
-
@param {''|'primary'|'success'|'warning'|'info'|'danger'|'dark'|'light'} [color=""] - Color of the input, Default: `""`
|
|
19
|
+
@param {string|''|'small'|'medium'|'large'} [size=""] - Size of the input, Default: `""`
|
|
20
|
+
@param {string|''|'primary'|'success'|'warning'|'info'|'danger'|'dark'|'light'} [color=""] - Color of the input, Default: `""`
|
|
21
21
|
@param {string} [style=""] - Inline CSS for input container, Default: `""`
|
|
22
22
|
@param {boolean} [readonly=false] - Marks component as read-only, Default: `false`
|
|
23
23
|
@param {boolean} [disabled=false] - Disables the component, Default: `false`
|
|
@@ -75,8 +75,22 @@ Default value: `<span>{option.label}</span>`
|
|
|
75
75
|
on:mousedown|preventDefault|stopPropagation={() =>
|
|
76
76
|
handleOptionMouseDown(option)}
|
|
77
77
|
on:mouseenter|preventDefault|stopPropagation={() => {
|
|
78
|
+
if (mouseTracker.preventSelect) return;
|
|
78
79
|
active_option = option;
|
|
79
80
|
}}
|
|
81
|
+
on:mousemove|preventDefault|stopPropagation={(e) => {
|
|
82
|
+
let { preventSelect, lastX, lastY } = mouseTracker;
|
|
83
|
+
if (
|
|
84
|
+
preventSelect &&
|
|
85
|
+
(lastX !== e.clientX || lastY !== e.clientY)
|
|
86
|
+
) {
|
|
87
|
+
mouseTracker.preventSelect = false;
|
|
88
|
+
active_option = option;
|
|
89
|
+
}
|
|
90
|
+
// mouse x,y is not in same position after the scrolling
|
|
91
|
+
mouseTracker.lastX = e.clientX;
|
|
92
|
+
mouseTracker.lastY = e.clientY;
|
|
93
|
+
}}
|
|
80
94
|
class="is-size-{list_text_size[size]}"
|
|
81
95
|
class:active={active_option === option}>
|
|
82
96
|
<!--
|
|
@@ -100,7 +114,8 @@ Default value: `<span>{option.label}</span>`
|
|
|
100
114
|
import { debounce } from "@kws3/ui/utils";
|
|
101
115
|
import { createEventDispatcher, onMount, tick } from "svelte";
|
|
102
116
|
import { createPopper } from "@popperjs/core";
|
|
103
|
-
import
|
|
117
|
+
import { makeSearchEngine } from "@kws3/ui/search";
|
|
118
|
+
import { scrollIntoActiveElement } from "../internal";
|
|
104
119
|
|
|
105
120
|
const sameWidthPopperModifier = {
|
|
106
121
|
name: "sameWidth",
|
|
@@ -120,10 +135,16 @@ Default value: `<span>{option.label}</span>`
|
|
|
120
135
|
|
|
121
136
|
const rootContainerId = "kws-overlay-root";
|
|
122
137
|
|
|
138
|
+
/**
|
|
139
|
+
* @typedef {import('@kws3/ui/types').ColorOptions} ColorOptions
|
|
140
|
+
* @typedef {import('@kws3/ui/types').SizeOptions} SizeOptions
|
|
141
|
+
*/
|
|
142
|
+
|
|
123
143
|
/**
|
|
124
144
|
* Value of the Input
|
|
125
145
|
*
|
|
126
146
|
* This property can be bound to, to fetch the current value
|
|
147
|
+
* @type {?string}
|
|
127
148
|
*/
|
|
128
149
|
export let value = "";
|
|
129
150
|
|
|
@@ -149,7 +170,7 @@ Default value: `<span>{option.label}</span>`
|
|
|
149
170
|
/**
|
|
150
171
|
* Filtered options to be displayed strictly based on search text or perform a fuzzy match.
|
|
151
172
|
* Fuzzy match will not work if `search` function is set, as the backend service is meant to do the matching.
|
|
152
|
-
* @type {'fuzzy'|'strict'}
|
|
173
|
+
* @type {string|'fuzzy'|'strict'}
|
|
153
174
|
*/
|
|
154
175
|
export let search_strategy = "fuzzy";
|
|
155
176
|
|
|
@@ -160,16 +181,17 @@ Default value: `<span>{option.label}</span>`
|
|
|
160
181
|
|
|
161
182
|
/**
|
|
162
183
|
* Score threshold for fuzzy search strategy, setting high score gives more fuzzy matches.
|
|
184
|
+
* @type {number}
|
|
163
185
|
*/
|
|
164
186
|
export let scoreThreshold = 5;
|
|
165
187
|
/**
|
|
166
188
|
* Size of the input
|
|
167
|
-
* @type {''
|
|
189
|
+
* @type {import('@kws3/ui/types').SizeOptions}
|
|
168
190
|
*/
|
|
169
191
|
export let size = "";
|
|
170
192
|
/**
|
|
171
193
|
* Color of the input
|
|
172
|
-
* @type {''
|
|
194
|
+
* @type {import('@kws3/ui/types').ColorOptions}
|
|
173
195
|
*/
|
|
174
196
|
export let color = "";
|
|
175
197
|
/**
|
|
@@ -222,10 +244,16 @@ Default value: `<span>{option.label}</span>`
|
|
|
222
244
|
active_option = "",
|
|
223
245
|
searching = true,
|
|
224
246
|
show_options = false,
|
|
247
|
+
mouseTracker = {
|
|
248
|
+
lastX: 0,
|
|
249
|
+
lastY: 0, // to check actual mouse is moving or not, for WebKit compatibility,
|
|
250
|
+
preventSelect: false, //prevent select by mouse when up or down key is pressed
|
|
251
|
+
},
|
|
225
252
|
filtered_options = [], //list of options filtered by search query
|
|
226
253
|
normalised_options = [], //list of options normalised
|
|
227
254
|
options_loading = false, //indictaes whether async search function is running
|
|
228
|
-
mounted = false
|
|
255
|
+
mounted = false, //indicates whether component is mounted
|
|
256
|
+
fuzzysearch = null;
|
|
229
257
|
|
|
230
258
|
let list_text_size = {
|
|
231
259
|
small: "7",
|
|
@@ -262,38 +290,11 @@ Default value: `<span>{option.label}</span>`
|
|
|
262
290
|
}
|
|
263
291
|
|
|
264
292
|
function triggerSearch(filters) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
let opts = [];
|
|
270
|
-
if (word) {
|
|
271
|
-
if (allow_fuzzy_match) {
|
|
272
|
-
opts = fuzzySearch(word, normalised_options);
|
|
273
|
-
} else {
|
|
274
|
-
opts = [...normalised_options].filter((item) => {
|
|
275
|
-
// filter out items that don't match `filter`
|
|
276
|
-
if (typeof item === "object" && item.value) {
|
|
277
|
-
return (
|
|
278
|
-
typeof item.value === "string" &&
|
|
279
|
-
item.value.toLowerCase().indexOf(word) > -1
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
cache[idx] = opts; // storing options to current index on cache
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
filtered_options = Object.values(cache) // get values from cache
|
|
290
|
-
.flat() // flatten array
|
|
291
|
-
.filter((v, i, self) => i === self.findIndex((t) => t.value === v.value)); // remove duplicates
|
|
292
|
-
|
|
293
|
-
if (highlighted_results && !allow_fuzzy_match) {
|
|
294
|
-
filtered_options = highlightMatches(filtered_options, filters);
|
|
293
|
+
if (allow_fuzzy_match) {
|
|
294
|
+
debouncedFuzzySearch(filters, [...normalised_options]);
|
|
295
|
+
} else {
|
|
296
|
+
searchInStrictMode(filters, [...normalised_options]);
|
|
295
297
|
}
|
|
296
|
-
setOptionsVisible(true);
|
|
297
298
|
}
|
|
298
299
|
|
|
299
300
|
function triggerExternalSearch(filters) {
|
|
@@ -303,19 +304,20 @@ Default value: `<span>{option.label}</span>`
|
|
|
303
304
|
return;
|
|
304
305
|
}
|
|
305
306
|
options_loading = true;
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
307
|
+
if (search) {
|
|
308
|
+
search(filters).then((_options) => {
|
|
309
|
+
searching = false;
|
|
310
|
+
options_loading = false;
|
|
311
|
+
tick().then(() => {
|
|
312
|
+
filtered_options = normaliseArraysToObjects(_options);
|
|
313
|
+
|
|
314
|
+
if (highlighted_results) {
|
|
315
|
+
filtered_options = highlightMatches(filtered_options, filters);
|
|
316
|
+
}
|
|
317
|
+
setOptionsVisible(true);
|
|
318
|
+
});
|
|
317
319
|
});
|
|
318
|
-
}
|
|
320
|
+
}
|
|
319
321
|
}
|
|
320
322
|
|
|
321
323
|
const debouncedTriggerSearch = debounce(triggerExternalSearch, 150, false);
|
|
@@ -324,18 +326,29 @@ Default value: `<span>{option.label}</span>`
|
|
|
324
326
|
POPPER = createPopper(el, dropdown, {
|
|
325
327
|
strategy: "fixed",
|
|
326
328
|
placement: "bottom-start",
|
|
329
|
+
// @ts-ignore
|
|
327
330
|
modifiers: [sameWidthPopperModifier],
|
|
328
331
|
});
|
|
329
332
|
|
|
330
|
-
if (allow_fuzzy_match
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
333
|
+
if (allow_fuzzy_match) {
|
|
334
|
+
let fuzzyOpts = {
|
|
335
|
+
analyzeSubTerms: true,
|
|
336
|
+
analyzeSubTermDepth: 10,
|
|
337
|
+
highlighting: {
|
|
338
|
+
after: "",
|
|
339
|
+
before: "",
|
|
340
|
+
},
|
|
341
|
+
};
|
|
335
342
|
if (highlighted_results) {
|
|
336
|
-
|
|
337
|
-
|
|
343
|
+
fuzzyOpts.highlighting.before = `<span class="h">`;
|
|
344
|
+
fuzzyOpts.highlighting.after = "</span>";
|
|
338
345
|
}
|
|
346
|
+
let searchOptions = {
|
|
347
|
+
search_key: "label",
|
|
348
|
+
scoreThreshold,
|
|
349
|
+
fuzzyOpts,
|
|
350
|
+
};
|
|
351
|
+
fuzzysearch = makeSearchEngine(searchOptions);
|
|
339
352
|
}
|
|
340
353
|
|
|
341
354
|
//normalize value
|
|
@@ -384,6 +397,14 @@ Default value: `<span>{option.label}</span>`
|
|
|
384
397
|
active_option = filtered_options[0];
|
|
385
398
|
else active_option = filtered_options[newActiveIdx];
|
|
386
399
|
}
|
|
400
|
+
|
|
401
|
+
tick().then(() => {
|
|
402
|
+
if (dropdown) {
|
|
403
|
+
mouseTracker.preventSelect = true;
|
|
404
|
+
let activeElem = dropdown.querySelector(".active");
|
|
405
|
+
scrollIntoActiveElement(dropdown, activeElem);
|
|
406
|
+
}
|
|
407
|
+
});
|
|
387
408
|
} else {
|
|
388
409
|
active_option = "";
|
|
389
410
|
searching = true;
|
|
@@ -446,25 +467,54 @@ Default value: `<span>{option.label}</span>`
|
|
|
446
467
|
return v && v.trim() ? v.toLowerCase().trim().split(/\s+/) : [];
|
|
447
468
|
}
|
|
448
469
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
470
|
+
const debouncedFuzzySearch = debounce(searchInFuzzyMode, 200, false);
|
|
471
|
+
|
|
472
|
+
function searchInFuzzyMode(filters, options) {
|
|
473
|
+
let cache = {};
|
|
474
|
+
//TODO - can optimize more for very long lists
|
|
475
|
+
filters.forEach((word, idx) => {
|
|
476
|
+
// iterate over each word in the search query
|
|
477
|
+
let opts = [];
|
|
478
|
+
if (word) {
|
|
479
|
+
let result = fuzzysearch(word, options);
|
|
480
|
+
opts = result;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
cache[idx] = opts; // storing options to current index on cache
|
|
459
484
|
});
|
|
485
|
+
setFilteredOptions(cache);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function searchInStrictMode(filters, options) {
|
|
489
|
+
let cache = {};
|
|
490
|
+
filters.forEach((word, idx) => {
|
|
491
|
+
// iterate over each word in the search query
|
|
492
|
+
let opts = [];
|
|
493
|
+
if (word) {
|
|
494
|
+
opts = options.filter((item) => {
|
|
495
|
+
// filter out items that don't match `filter`
|
|
496
|
+
if (typeof item === "object" && item.value) {
|
|
497
|
+
return (
|
|
498
|
+
typeof item.value === "string" &&
|
|
499
|
+
item.value.toLowerCase().indexOf(word) > -1
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
}
|
|
460
504
|
|
|
461
|
-
|
|
462
|
-
|
|
505
|
+
cache[idx] = opts; // storing options to current index on cache
|
|
506
|
+
});
|
|
507
|
+
setFilteredOptions(cache, filters);
|
|
508
|
+
}
|
|
463
509
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
510
|
+
function setFilteredOptions(cache, filters) {
|
|
511
|
+
filtered_options = Object.values(cache) // get values from cache
|
|
512
|
+
.flat() // flatten array
|
|
513
|
+
.filter((v, i, self) => i === self.findIndex((t) => t.value === v.value)); // remove duplicates
|
|
467
514
|
|
|
468
|
-
|
|
515
|
+
if (highlighted_results && !allow_fuzzy_match) {
|
|
516
|
+
filtered_options = highlightMatches(filtered_options, filters);
|
|
517
|
+
}
|
|
518
|
+
setOptionsVisible(true);
|
|
469
519
|
}
|
|
470
520
|
</script>
|
package/forms/Datepicker.svelte
CHANGED
|
@@ -8,10 +8,10 @@ In `range_mode`, the expected format is `yyyy-mm-dd to yyyy-mm-dd`
|
|
|
8
8
|
|
|
9
9
|
This property can be bound to, to fetch the selected date or date range. Output is in the same format as input., Default: `""`
|
|
10
10
|
@param {string} [style=""] - Inline CSS for the input, Default: `""`
|
|
11
|
-
@param {''|'primary'|'warning'|'info'|'danger'|'dark'|'light'} [color=""] - Colour of the Date picker input, Default: `""`
|
|
11
|
+
@param {string|''|'primary'|'warning'|'info'|'danger'|'dark'|'light'} [color=""] - Colour of the Date picker input, Default: `""`
|
|
12
12
|
@param {boolean} [disabled=false] - Disables the component, Default: `false`
|
|
13
13
|
@param {string} [placeholder="Select Date.."] - Placeholder text for the input, Default: `"Select Date.."`
|
|
14
|
-
@param {'primary'|'warning'|'info'|'danger'|'dark'|'light'} [calendar_color="primary"] - Colour of the Calendar, Default: `"primary"`
|
|
14
|
+
@param {string|'primary'|'warning'|'info'|'danger'|'dark'|'light'} [calendar_color="primary"] - Colour of the Calendar, Default: `"primary"`
|
|
15
15
|
@param {any} [min_date=null] - Set earliest selectable date as an object or string
|
|
16
16
|
|
|
17
17
|
**Example:** `"2021-06-06"` or `"(new Date('2021-01-01'))"`, Default: `null`
|
|
@@ -55,11 +55,15 @@ See: https://flatpickr.js.org/options/, Default: `{}`
|
|
|
55
55
|
on:yearChange={fireYearChange} />
|
|
56
56
|
|
|
57
57
|
<script>
|
|
58
|
-
import { datepicker } from "./actions";
|
|
59
58
|
import { createEventDispatcher } from "svelte";
|
|
59
|
+
import { datepicker } from "./actions";
|
|
60
60
|
|
|
61
61
|
const fire = createEventDispatcher();
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* @typedef {import('@kws3/ui/types').ColorOptions} ColorOptions
|
|
65
|
+
*/
|
|
66
|
+
|
|
63
67
|
/**
|
|
64
68
|
* Accepts a date value in the format `yyyy-mm-dd`
|
|
65
69
|
*
|
|
@@ -74,20 +78,24 @@ See: https://flatpickr.js.org/options/, Default: `{}`
|
|
|
74
78
|
export let style = "";
|
|
75
79
|
/**
|
|
76
80
|
* Colour of the Date picker input
|
|
77
|
-
* @type {
|
|
81
|
+
* @type {ColorOptions} color
|
|
78
82
|
*/
|
|
79
83
|
export let color = "";
|
|
80
84
|
/**
|
|
81
85
|
* Disables the component
|
|
82
86
|
*/
|
|
83
87
|
export let disabled = false;
|
|
88
|
+
/**
|
|
89
|
+
* Make input value read-only
|
|
90
|
+
*/
|
|
91
|
+
export let readonly = false;
|
|
84
92
|
/**
|
|
85
93
|
* Placeholder text for the input
|
|
86
94
|
*/
|
|
87
95
|
export let placeholder = "Select Date..";
|
|
88
96
|
/**
|
|
89
97
|
* Colour of the Calendar
|
|
90
|
-
* @type {'
|
|
98
|
+
* @type {Exclude<ColorOptions, ''>}
|
|
91
99
|
*/
|
|
92
100
|
export let calendar_color = "primary";
|
|
93
101
|
/**
|
|
@@ -140,9 +148,13 @@ See: https://flatpickr.js.org/options/, Default: `{}`
|
|
|
140
148
|
min_date,
|
|
141
149
|
max_date,
|
|
142
150
|
options,
|
|
151
|
+
readonly,
|
|
143
152
|
fillOptions();
|
|
144
153
|
|
|
145
154
|
function fillOptions() {
|
|
155
|
+
/**
|
|
156
|
+
* @type {object}
|
|
157
|
+
*/
|
|
146
158
|
let _opts = Object.assign(
|
|
147
159
|
{
|
|
148
160
|
color: calendar_color,
|
|
@@ -164,6 +176,11 @@ See: https://flatpickr.js.org/options/, Default: `{}`
|
|
|
164
176
|
if (max_date) {
|
|
165
177
|
_opts.maxDate = max_date;
|
|
166
178
|
}
|
|
179
|
+
|
|
180
|
+
_opts.clickOpens = true;
|
|
181
|
+
if (readonly) {
|
|
182
|
+
_opts.clickOpens = false;
|
|
183
|
+
}
|
|
167
184
|
opts = _opts;
|
|
168
185
|
}
|
|
169
186
|
|
|
@@ -30,22 +30,22 @@ Should be used with `bind` from parent component, Default: `false`
|
|
|
30
30
|
<li>
|
|
31
31
|
<span class="help expanded">
|
|
32
32
|
<span
|
|
33
|
-
class="tag is-small is-normal is-light pv-identifier {opt
|
|
33
|
+
class="tag is-small is-normal is-light pv-identifier {opt['passed']
|
|
34
34
|
? 'is-success'
|
|
35
|
-
: 'is-danger'}">{opt
|
|
36
|
-
<span class="pv-text">{opt
|
|
35
|
+
: 'is-danger'}">{opt["identifier"]}</span>
|
|
36
|
+
<span class="pv-text">{opt["text"]}</span>
|
|
37
37
|
<Icon
|
|
38
38
|
class="pv-icon"
|
|
39
|
-
icon={opt
|
|
40
|
-
color={opt
|
|
39
|
+
icon={opt["passed"] ? "check" : "ban"}
|
|
40
|
+
color={opt["passed"] ? "success" : "danger"} />
|
|
41
41
|
</span>
|
|
42
42
|
<span class="summarized">
|
|
43
43
|
<span
|
|
44
|
-
data-tooltip={opt
|
|
44
|
+
data-tooltip={opt["text"]}
|
|
45
45
|
data-tippy-hideOnClick="false"
|
|
46
|
-
class="tag is-small is-normal is-light pv-identifier {opt
|
|
46
|
+
class="tag is-small is-normal is-light pv-identifier {opt['passed']
|
|
47
47
|
? 'is-success'
|
|
48
|
-
: 'is-danger'}">{opt
|
|
48
|
+
: 'is-danger'}">{opt["identifier"]}</span>
|
|
49
49
|
</span>
|
|
50
50
|
</li>
|
|
51
51
|
{/each}
|
|
@@ -4,11 +4,20 @@ export default function (password, options) {
|
|
|
4
4
|
overall: false,
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('@kws3/ui/types').ValidatePasswordOptions} ValidatePasswordOptions - contains ValidatePassword options
|
|
9
|
+
*/
|
|
10
|
+
|
|
7
11
|
result.items = (options || []).slice().map((_opt) => {
|
|
12
|
+
/** @type {ValidatePasswordOptions} */
|
|
8
13
|
const opt = Object.assign({}, _opt);
|
|
9
14
|
if (opt && opt.active) {
|
|
10
15
|
if (opt.name === "kws_pv_min_length") {
|
|
11
|
-
if (
|
|
16
|
+
if (
|
|
17
|
+
typeof opt.value != "undefined" &&
|
|
18
|
+
password &&
|
|
19
|
+
password.length >= opt.value
|
|
20
|
+
) {
|
|
12
21
|
opt.passed = true;
|
|
13
22
|
}
|
|
14
23
|
} else {
|
|
@@ -24,7 +33,9 @@ export default function (password, options) {
|
|
|
24
33
|
});
|
|
25
34
|
|
|
26
35
|
result.overall =
|
|
27
|
-
result.items.filter(
|
|
36
|
+
result.items.filter(
|
|
37
|
+
(/** @type {ValidatePasswordOptions} */ el) => el.passed
|
|
38
|
+
).length === result.items.length;
|
|
28
39
|
|
|
29
40
|
return result;
|
|
30
41
|
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@param {string|''|'small'|'medium'|'large'} [size="small"] - Size of the input, Default: `"small"`
|
|
6
|
+
@param {string|''|'primary'|'success'|'warning'|'info'|'danger'|'dark'|'light'} [color=""] - Color of the input, Default: `""`
|
|
7
|
+
@param {string} [placeholder="Search"] - Placeholder text for the input, Default: `"Search"`
|
|
8
|
+
@param {boolean} [readonly=false] - Marks component as read-only, Default: `false`
|
|
9
|
+
@param {boolean} [disabled=false] - Disables the component, Default: `false`
|
|
10
|
+
@param {array} [options=[]] - Array of objects., Default: `[]`
|
|
11
|
+
@param {array} [searchableKeys=[]] - array of object properties to search in., Default: `[]`
|
|
12
|
+
@param {boolean} [highlighted_results=true] - Whether to show the highlighted or plain results in the dropdown., Default: `true`
|
|
13
|
+
@param {number} [scoreThreshold=2] - Score threshold for fuzzy search strategy, setting high score gives more fuzzy matches., Default: `2`
|
|
14
|
+
@param {boolean} [word_match=false] - Whether to match against each word seperatly or whole sentence in flow., Default: `false`
|
|
15
|
+
@param {string} [style=""] - Inline CSS for the input, Default: `""`
|
|
16
|
+
@param {string} [class=""] - CSS classes for the input, Default: `""`
|
|
17
|
+
|
|
18
|
+
-->
|
|
19
|
+
<div
|
|
20
|
+
class="
|
|
21
|
+
field has-addons is-marginless
|
|
22
|
+
{readonly ? 'is-readonly' : ''}
|
|
23
|
+
{disabled ? 'is-disabled' : ''}
|
|
24
|
+
">
|
|
25
|
+
<div class="control is-expanded has-icons-left">
|
|
26
|
+
<input
|
|
27
|
+
class="input is-{size} is-{color} {klass}"
|
|
28
|
+
{placeholder}
|
|
29
|
+
{disabled}
|
|
30
|
+
{readonly}
|
|
31
|
+
{style}
|
|
32
|
+
bind:value={keywords}
|
|
33
|
+
on:keyup={debouncedSearch} />
|
|
34
|
+
<Icon icon="search" size="small" class="is-left" />
|
|
35
|
+
</div>
|
|
36
|
+
{#if keywords}
|
|
37
|
+
<div class="control">
|
|
38
|
+
<button class="button is-danger is-{size}" type="button" on:click={reset}>
|
|
39
|
+
<Icon icon="times" size="small" />
|
|
40
|
+
</button>
|
|
41
|
+
</div>
|
|
42
|
+
{/if}
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<script>
|
|
46
|
+
import { Icon } from "@kws3/ui";
|
|
47
|
+
import { debounce } from "@kws3/ui/utils";
|
|
48
|
+
import { onDestroy, onMount } from "svelte";
|
|
49
|
+
import { makeSearchEngine } from "@kws3/ui/search";
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Size of the input
|
|
53
|
+
* @type {import('@kws3/ui/types').SizeOptions} size
|
|
54
|
+
*/
|
|
55
|
+
export let size = "small";
|
|
56
|
+
/**
|
|
57
|
+
* Color of the input
|
|
58
|
+
* @type {import('@kws3/ui/types').ColorOptions} color
|
|
59
|
+
*/
|
|
60
|
+
export let color = "";
|
|
61
|
+
/**
|
|
62
|
+
* Placeholder text for the input
|
|
63
|
+
*/
|
|
64
|
+
export let placeholder = "Search";
|
|
65
|
+
/**
|
|
66
|
+
* Marks component as read-only
|
|
67
|
+
*/
|
|
68
|
+
export let readonly = false;
|
|
69
|
+
/**
|
|
70
|
+
* Disables the component
|
|
71
|
+
*/
|
|
72
|
+
export let disabled = false;
|
|
73
|
+
/**
|
|
74
|
+
* Array of objects.
|
|
75
|
+
* @type {array}
|
|
76
|
+
*/
|
|
77
|
+
export let options = [];
|
|
78
|
+
/**
|
|
79
|
+
* array of object properties to search in.
|
|
80
|
+
* @type {array}
|
|
81
|
+
*/
|
|
82
|
+
export let searchableKeys = [];
|
|
83
|
+
/**
|
|
84
|
+
* Whether to show the highlighted or plain results in the dropdown.
|
|
85
|
+
*/
|
|
86
|
+
export let highlighted_results = true;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Score threshold for fuzzy search strategy, setting high score gives more fuzzy matches.
|
|
90
|
+
* @type {number}
|
|
91
|
+
*/
|
|
92
|
+
export let scoreThreshold = 2;
|
|
93
|
+
/**
|
|
94
|
+
* Whether to match against each word seperatly or whole sentence in flow.
|
|
95
|
+
*/
|
|
96
|
+
export let word_match = false;
|
|
97
|
+
/**
|
|
98
|
+
* Inline CSS for the input
|
|
99
|
+
*/
|
|
100
|
+
export let style = "";
|
|
101
|
+
/**
|
|
102
|
+
* CSS classes for the input
|
|
103
|
+
*/
|
|
104
|
+
let klass = "";
|
|
105
|
+
export { klass as class };
|
|
106
|
+
|
|
107
|
+
let keywords = "",
|
|
108
|
+
orginalItems = [],
|
|
109
|
+
fuzzysearch = null;
|
|
110
|
+
|
|
111
|
+
const debouncedSearch = debounce(search, 300);
|
|
112
|
+
|
|
113
|
+
const sanitizeValue = (v) =>
|
|
114
|
+
v && v.trim() ? v.toLowerCase().trim().split(/\s+/) : [];
|
|
115
|
+
|
|
116
|
+
onMount(() => {
|
|
117
|
+
let fuzzyOpts = {
|
|
118
|
+
analyzeSubTerms: true,
|
|
119
|
+
analyzeSubTermDepth: 10,
|
|
120
|
+
highlighting: {
|
|
121
|
+
after: "",
|
|
122
|
+
before: "",
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
if (highlighted_results) {
|
|
126
|
+
fuzzyOpts.highlighting.before = `<span class="h">`;
|
|
127
|
+
fuzzyOpts.highlighting.after = "</span>";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let searchOptions = {
|
|
131
|
+
search_key: searchableKeys,
|
|
132
|
+
scoreThreshold,
|
|
133
|
+
fuzzyOpts,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
fuzzysearch = makeSearchEngine(searchOptions);
|
|
137
|
+
|
|
138
|
+
if (word_match) {
|
|
139
|
+
options.forEach((item, i) => {
|
|
140
|
+
item._uid = i;
|
|
141
|
+
});
|
|
142
|
+
orginalItems = [...options];
|
|
143
|
+
} else {
|
|
144
|
+
orginalItems = [...options];
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
onDestroy(reset);
|
|
149
|
+
|
|
150
|
+
function search() {
|
|
151
|
+
if (!keywords) {
|
|
152
|
+
reset();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
let result = [];
|
|
156
|
+
|
|
157
|
+
if (word_match) {
|
|
158
|
+
let cache = {},
|
|
159
|
+
filters = sanitizeValue(keywords);
|
|
160
|
+
filters.forEach((word, idx) => {
|
|
161
|
+
// iterate over each word in the search query
|
|
162
|
+
let opts = [];
|
|
163
|
+
if (word) opts = fuzzysearch(word, orginalItems);
|
|
164
|
+
cache[idx] = opts; // storing options to current index on cache
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
result = Object.values(cache) // get values from cache
|
|
168
|
+
.flat()
|
|
169
|
+
.filter((v, i, self) => i === self.findIndex((t) => t._uid === v._uid)); // flatten array
|
|
170
|
+
} else {
|
|
171
|
+
result = fuzzysearch(keywords, orginalItems);
|
|
172
|
+
}
|
|
173
|
+
options = result;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function reset() {
|
|
177
|
+
keywords = "";
|
|
178
|
+
options = [...orginalItems];
|
|
179
|
+
}
|
|
180
|
+
</script>
|