@kws3/ui 1.9.2 → 2.0.1

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.
Files changed (81) hide show
  1. package/CHANGELOG.mdx +74 -48
  2. package/buttons/ConfirmButton.svelte +11 -3
  3. package/buttons/DeleteButton.svelte +2 -3
  4. package/buttons/ProcessButton.svelte +3 -4
  5. package/buttons/SubmitButton.svelte +11 -3
  6. package/charts/AreaChart.svelte +1 -1
  7. package/charts/BarChart.svelte +1 -1
  8. package/charts/Chart.svelte +1 -0
  9. package/charts/DonutChart.svelte +1 -1
  10. package/charts/LineChart.svelte +1 -1
  11. package/charts/MixedChart.svelte +1 -1
  12. package/charts/PieChart.svelte +1 -1
  13. package/charts/RadialChart.svelte +1 -1
  14. package/charts/utils.js +1 -0
  15. package/controls/Checkbox.svelte +10 -4
  16. package/controls/FileUpload.svelte +14 -8
  17. package/controls/NumberInput.svelte +19 -10
  18. package/controls/Radio.svelte +8 -2
  19. package/controls/RangeSlider.svelte +8 -2
  20. package/controls/Toggle.svelte +9 -2
  21. package/controls/ToggleButtons.svelte +10 -3
  22. package/datagrid/DataSearch/DataSearch.svelte +1 -1
  23. package/datagrid/GridView/GridCell.svelte +3 -0
  24. package/datagrid/GridView/GridRow.svelte +15 -0
  25. package/datagrid/GridView/GridView.svelte +21 -3
  26. package/datagrid/Pagination/Pagination.svelte +3 -3
  27. package/datagrid/TileView/TileView.svelte +46 -5
  28. package/datagrid/TileView/TileViewItem.svelte +4 -0
  29. package/form/index.js +160 -0
  30. package/forms/AutoComplete.svelte +78 -33
  31. package/forms/Datepicker.svelte +22 -5
  32. package/forms/PasswordValidator/PasswordValidator.svelte +8 -8
  33. package/forms/PasswordValidator/validatePassword.js +13 -2
  34. package/forms/SearchInput.svelte +180 -0
  35. package/forms/Timepicker.svelte +69 -4
  36. package/forms/actions.js +21 -15
  37. package/forms/colorpicker/Colorpicker.js +28 -3
  38. package/forms/colorpicker/Colorpicker.svelte +2 -2
  39. package/forms/select/MultiSelect.svelte +89 -30
  40. package/forms/select/SearchableSelect.svelte +6 -5
  41. package/helpers/CardModal.svelte +2 -1
  42. package/helpers/ClipboardCopier.svelte +4 -1
  43. package/helpers/Dialog/Dialog.svelte +13 -8
  44. package/helpers/Dialog/index.js +6 -0
  45. package/helpers/Divider.svelte +2 -2
  46. package/helpers/FloatingUI/Floatie.svelte +6 -6
  47. package/helpers/FloatingUI/index.js +2 -1
  48. package/helpers/Icon.svelte +25 -9
  49. package/helpers/Loader.svelte +10 -3
  50. package/helpers/Message.svelte +2 -2
  51. package/helpers/Modal.svelte +2 -1
  52. package/helpers/Notification.svelte +1 -1
  53. package/helpers/Popover.svelte +4 -4
  54. package/helpers/ScrollableList.svelte +12 -8
  55. package/helpers/Skeleton.svelte +4 -1
  56. package/helpers/Timeline/Timeline.svelte +1 -1
  57. package/helpers/Timeline/TimelineItem.svelte +5 -5
  58. package/helpers/Tooltip.js +1 -1
  59. package/index.js +10 -4
  60. package/{utils → internal}/fuzzy.js +64 -65
  61. package/internal/index.js +27 -0
  62. package/internal/scrollIntoActiveElement.js +22 -0
  63. package/keyboard/index.js +94 -0
  64. package/package.json +6 -3
  65. package/{utils/resizeObserver.js → resizeObserver/index.js} +0 -0
  66. package/search/index.js +52 -0
  67. package/settings.js +1 -1
  68. package/sliding-panes/SlidingPane.svelte +1 -4
  69. package/styles/AutoComplete.scss +2 -1
  70. package/styles/Datepicker.scss +1 -1
  71. package/styles/Grid.scss +14 -0
  72. package/styles/Pagination.scss +8 -5
  73. package/styles/Select.scss +2 -1
  74. package/transitions/components/Scale.svelte +1 -0
  75. package/transitions/components/getEasing.js +18 -5
  76. package/types/ambient.d.ts +16 -0
  77. package/types/index.d.ts +46 -0
  78. package/types/type-defs/index.ts +14 -0
  79. package/utils/index.js +110 -9
  80. package/utils/fuzzysearch.js +0 -42
  81. package/utils/keyboard-events.js +0 -32
@@ -2,9 +2,9 @@
2
2
  @component
3
3
 
4
4
 
5
- @param {string} [value=""] - Value of the Input
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 { fuzzy, fuzzysearch } from "../utils/fuzzysearch";
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 {''|'small'|'medium'|'large'}
189
+ * @type {import('@kws3/ui/types').SizeOptions}
168
190
  */
169
191
  export let size = "";
170
192
  /**
171
193
  * Color of the input
172
- * @type {''|'primary'|'success'|'warning'|'info'|'danger'|'dark'|'light'}
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; //indicates whether component is mounted
255
+ mounted = false, //indicates whether component is mounted
256
+ fuzzysearch = null;
229
257
 
230
258
  let list_text_size = {
231
259
  small: "7",
@@ -276,19 +304,20 @@ Default value: `<span>{option.label}</span>`
276
304
  return;
277
305
  }
278
306
  options_loading = true;
279
- // filtered_options = [];
280
- search(filters).then((_options) => {
281
- searching = false;
282
- options_loading = false;
283
- tick().then(() => {
284
- filtered_options = normaliseArraysToObjects(_options);
285
-
286
- if (highlighted_results) {
287
- filtered_options = highlightMatches(filtered_options, filters);
288
- }
289
- setOptionsVisible(true);
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
+ });
290
319
  });
291
- });
320
+ }
292
321
  }
293
322
 
294
323
  const debouncedTriggerSearch = debounce(triggerExternalSearch, 150, false);
@@ -297,18 +326,29 @@ Default value: `<span>{option.label}</span>`
297
326
  POPPER = createPopper(el, dropdown, {
298
327
  strategy: "fixed",
299
328
  placement: "bottom-start",
329
+ // @ts-ignore
300
330
  modifiers: [sameWidthPopperModifier],
301
331
  });
302
332
 
303
- if (allow_fuzzy_match && fuzzy) {
304
- fuzzy.analyzeSubTerms = true;
305
- fuzzy.analyzeSubTermDepth = 10;
306
- fuzzy.highlighting.before = "";
307
- fuzzy.highlighting.after = "";
333
+ if (allow_fuzzy_match) {
334
+ let fuzzyOpts = {
335
+ analyzeSubTerms: true,
336
+ analyzeSubTermDepth: 10,
337
+ highlighting: {
338
+ after: "",
339
+ before: "",
340
+ },
341
+ };
308
342
  if (highlighted_results) {
309
- fuzzy.highlighting.before = `<span class="h">`;
310
- fuzzy.highlighting.after = "</span>";
343
+ fuzzyOpts.highlighting.before = `<span class="h">`;
344
+ fuzzyOpts.highlighting.after = "</span>";
311
345
  }
346
+ let searchOptions = {
347
+ search_key: "label",
348
+ scoreThreshold,
349
+ fuzzyOpts,
350
+ };
351
+ fuzzysearch = makeSearchEngine(searchOptions);
312
352
  }
313
353
 
314
354
  //normalize value
@@ -357,6 +397,14 @@ Default value: `<span>{option.label}</span>`
357
397
  active_option = filtered_options[0];
358
398
  else active_option = filtered_options[newActiveIdx];
359
399
  }
400
+
401
+ tick().then(() => {
402
+ if (dropdown) {
403
+ mouseTracker.preventSelect = true;
404
+ let activeElem = dropdown.querySelector(".active");
405
+ scrollIntoActiveElement(dropdown, activeElem);
406
+ }
407
+ });
360
408
  } else {
361
409
  active_option = "";
362
410
  searching = true;
@@ -428,10 +476,7 @@ Default value: `<span>{option.label}</span>`
428
476
  // iterate over each word in the search query
429
477
  let opts = [];
430
478
  if (word) {
431
- let result = fuzzysearch(word, options, {
432
- search_key: "label",
433
- scoreThreshold,
434
- });
479
+ let result = fuzzysearch(word, options);
435
480
  opts = result;
436
481
  }
437
482
 
@@ -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 {''|'primary'|'warning'|'info'|'danger'|'dark'|'light'}
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 {'primary'|'warning'|'info'|'danger'|'dark'|'light'}
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.passed
33
+ class="tag is-small is-normal is-light pv-identifier {opt['passed']
34
34
  ? 'is-success'
35
- : 'is-danger'}">{opt.identifier}</span>
36
- <span class="pv-text">{opt.text}</span>
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.passed ? "check" : "ban"}
40
- color={opt.passed ? "success" : "danger"} />
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.text}
44
+ data-tooltip={opt["text"]}
45
45
  data-tippy-hideOnClick="false"
46
- class="tag is-small is-normal is-light pv-identifier {opt.passed
46
+ class="tag is-small is-normal is-light pv-identifier {opt['passed']
47
47
  ? 'is-success'
48
- : 'is-danger'}">{opt.identifier}</span>
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 (password && password.length >= opt.value) {
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((el) => el.passed).length === result.items.length;
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>
@@ -14,6 +14,10 @@ This property can be bound to, to fetch the selected time. Output is in the same
14
14
  @param {'primary'|'warning'|'info'|'danger'|'dark'|'light'} [ui_color="primary"] - Colour of popup time selection UI, Default: `"primary"`
15
15
  @param {boolean} [time_24hr=false] - Display time selection UI in 24hr format, Default: `false`
16
16
  @param {object} [options={}] - Extended set of options as supported by Flatpicker
17
+ @param {any} [min_time=null] - Set earliest selectable time as string
18
+ **Example:** `"12:00 PM"` Default: `null`
19
+ @param {any} [max_time=null] - Set latest selectable time as string
20
+ **Example:** `"12:00 AM"` Default: `null`
17
21
 
18
22
  See: https://flatpickr.js.org/options/, Default: `{}`
19
23
  @param {string} [class=""] - CSS classes for the input, Default: `""`
@@ -43,11 +47,15 @@ See: https://flatpickr.js.org/options/, Default: `{}`
43
47
  on:close={fireClose} />
44
48
 
45
49
  <script>
46
- import { timepicker } from "./actions";
47
50
  import { createEventDispatcher } from "svelte";
51
+ import { timepicker } from "./actions";
48
52
 
49
53
  const fire = createEventDispatcher();
50
54
 
55
+ /**
56
+ * @typedef {import('@kws3/ui/types').ColorOptions} ColorOptions
57
+ */
58
+
51
59
  /**
52
60
  * Accepts a date value in the format `H:i`
53
61
  *
@@ -62,7 +70,7 @@ See: https://flatpickr.js.org/options/, Default: `{}`
62
70
  export let style = "";
63
71
  /**
64
72
  * Colour of the Time picker input
65
- * @type {''|'primary'|'warning'|'info'|'danger'|'dark'|'light'}
73
+ * @type {ColorOptions} color
66
74
  */
67
75
  export let color = "";
68
76
  /**
@@ -75,7 +83,7 @@ See: https://flatpickr.js.org/options/, Default: `{}`
75
83
  export let placeholder = "Select Time..";
76
84
  /**
77
85
  * Colour of popup time selection UI
78
- * @type {'primary'|'warning'|'info'|'danger'|'dark'|'light'}
86
+ * @type {Exclude<ColorOptions, ''>}
79
87
  */
80
88
  export let ui_color = "primary";
81
89
 
@@ -84,23 +92,64 @@ See: https://flatpickr.js.org/options/, Default: `{}`
84
92
  */
85
93
  export let time_24hr = false;
86
94
 
95
+ /**
96
+ * Set earliest selectable time as string
97
+ *
98
+ * **Example:** `"01:00 PM"` or "13:00"`
99
+ * @type {any}
100
+ */
101
+ export let min_time = null;
102
+ /**
103
+ * Set latest selectable time as string
104
+ *
105
+ * **Example:** `"03:00 PM"` or "15:00"`
106
+ * @type {any}
107
+ */
108
+ export let max_time = null;
109
+
87
110
  /**
88
111
  * Extended set of options as supported by Flatpicker
89
112
  *
90
113
  * See: https://flatpickr.js.org/options/
91
114
  */
92
115
  export let options = {};
116
+
117
+ /**
118
+ * Make input value read-only
119
+ */
120
+ export let readonly = false;
121
+
93
122
  /**
94
123
  * CSS classes for the input
95
124
  */
125
+
96
126
  let klass = "";
97
127
  export { klass as class };
98
128
 
99
129
  let opts;
100
130
 
101
- $: ui_color, options, time_24hr, fillOptions();
131
+ $: ui_color, options, time_24hr, min_time, max_time, readonly, fillOptions();
132
+
133
+ const convertTime12to24 = (time12h) => {
134
+ const [time, modifier] = time12h.split(" ");
135
+ let [hours, minutes] = time.split(":");
136
+ if (hours === "12") {
137
+ hours = "00";
138
+ }
139
+ if (modifier === "PM") {
140
+ hours = parseInt(hours, 10) + 12;
141
+ }
142
+ return {
143
+ hour: String(hours),
144
+ minute: String(minutes),
145
+ time: `${hours}:${minutes}`,
146
+ };
147
+ };
102
148
 
103
149
  function fillOptions() {
150
+ /**
151
+ * @type {object}
152
+ */
104
153
  let _opts = Object.assign(
105
154
  {
106
155
  color: ui_color,
@@ -109,6 +158,22 @@ See: https://flatpickr.js.org/options/, Default: `{}`
109
158
  options
110
159
  );
111
160
 
161
+ if (min_time) {
162
+ let _minTime24 = convertTime12to24(min_time);
163
+ _opts.minTime = _minTime24.time;
164
+ _opts.defaultHour = _minTime24.hour;
165
+ _opts.defaultMinute = _minTime24.minute;
166
+ }
167
+
168
+ if (max_time) {
169
+ let _maxTime24 = convertTime12to24(max_time);
170
+ _opts.maxTime = _maxTime24.time;
171
+ }
172
+
173
+ _opts.clickOpens = true;
174
+ if (readonly) {
175
+ _opts.clickOpens = false;
176
+ }
112
177
  opts = _opts;
113
178
  }
114
179