@kws3/ui 1.8.2 → 1.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/CHANGELOG.mdx +16 -0
- package/buttons/SubmitButton.svelte +1 -1
- package/datagrid/GridView/GridRow.svelte +16 -7
- package/datagrid/GridView/GridView.svelte +14 -2
- package/datagrid/TileView/TileView.svelte +12 -0
- package/datagrid/TileView/TileViewItem.svelte +11 -5
- package/forms/AutoComplete.svelte +468 -0
- package/forms/select/MultiSelect.svelte +92 -31
- package/index.js +1 -0
- package/package.json +3 -2
- package/styles/AutoComplete.scss +132 -0
- package/styles/Grid.scss +1 -0
package/CHANGELOG.mdx
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## 1.9.0
|
|
2
|
+
- Use new fuzzy algorithm for `AutoComplete`,`MultiSelect` and `SearchableSelect` component
|
|
3
|
+
- Add property `scoreThreshold` in `AutoComplete`,`MultiSelect` and `SearchableSelect` component to control search accuracy.
|
|
4
|
+
- Bugfix: Keep `SubmitButton` disabled while it's not ready to submit yet
|
|
5
|
+
|
|
6
|
+
## 1.8.4
|
|
7
|
+
- New `AutoComplete` component
|
|
8
|
+
- Make options text size match the input `size` in `MultiSelect` and `SearchableSelect`.
|
|
9
|
+
- Prevent default arrow up/down behaviour on `MultiSelect` and `SearchableSelect` when options dropdown is open.
|
|
10
|
+
|
|
11
|
+
## 1.8.3
|
|
12
|
+
- Allow `clickableRows` and `bulk_actions` to work at the same time on `GridView`
|
|
13
|
+
- Various bugfixes on `GridRow`
|
|
14
|
+
- New `visualActivationOnClick` prop for `GridView` and `TileView`
|
|
15
|
+
- Change the way click activation works on `GridView` and `TileView` rows. Now only one row can be activated at a time
|
|
16
|
+
|
|
1
17
|
## 1.8.2
|
|
2
18
|
- Usability fixes for `NumberInput`
|
|
3
19
|
- New `input_only`, `force_integer`, `style` and `class` props for `NumberInput`
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
@param {number} [row_index=0] - Row index value, Default: `0`
|
|
6
6
|
@param {object} [row={}] - Contains all the column values in a row, Default: `{}`
|
|
7
|
-
@param {boolean} [
|
|
7
|
+
@param {boolean} [visualActivationOnClick=true] - Determines whether clickable rows activate visually on click, Default: `true`
|
|
8
|
+
@param {object} [activatedId=null] - Unique id of row that is activated, Default: `null`
|
|
8
9
|
@param {object} [isVisible={}] - Determines whether column is visible or not, Default: `{}`
|
|
9
10
|
@param {boolean} [clickableRows=false] - Determines whether the row is clickable or not, Default: `false`
|
|
10
11
|
@param {object} [transforms={}] - Contains all custom values for each columns, Default: `{}`
|
|
@@ -29,10 +30,14 @@
|
|
|
29
30
|
<tr
|
|
30
31
|
in:fly={{ x: 20, delay: 25 * row_index }}
|
|
31
32
|
on:click|stopPropagation={rowClick}
|
|
32
|
-
class:is-selected={
|
|
33
|
+
class:is-selected={activated && visualActivationOnClick}
|
|
33
34
|
class:is-checked={checked}>
|
|
34
35
|
{#if bulk_actions}
|
|
35
|
-
<td
|
|
36
|
+
<td
|
|
37
|
+
style="vertical-align:middle;"
|
|
38
|
+
on:click={(e) => {
|
|
39
|
+
clickableRows && e.stopImmediatePropagation();
|
|
40
|
+
}}>
|
|
36
41
|
<Checkbox
|
|
37
42
|
size={selectCheckboxSize}
|
|
38
43
|
color={selectCheckboxColor}
|
|
@@ -58,7 +63,7 @@
|
|
|
58
63
|
{:else}
|
|
59
64
|
<tr
|
|
60
65
|
on:click|stopPropagation={rowClick}
|
|
61
|
-
class:is-selected={
|
|
66
|
+
class:is-selected={activated && visualActivationOnClick}
|
|
62
67
|
class:is-checked={checked}>
|
|
63
68
|
{#if bulk_actions}
|
|
64
69
|
<td style="vertical-align:middle;">
|
|
@@ -103,9 +108,13 @@
|
|
|
103
108
|
*/
|
|
104
109
|
row = {},
|
|
105
110
|
/**
|
|
106
|
-
* Determines whether
|
|
111
|
+
* Determines whether clickable rows activate visually on click
|
|
112
|
+
*/
|
|
113
|
+
visualActivationOnClick = true,
|
|
114
|
+
/**
|
|
115
|
+
* Unique id of row that is activated
|
|
107
116
|
*/
|
|
108
|
-
|
|
117
|
+
activatedId = null,
|
|
109
118
|
/**
|
|
110
119
|
* Determines whether column is visible or not
|
|
111
120
|
*/
|
|
@@ -161,6 +170,7 @@
|
|
|
161
170
|
};
|
|
162
171
|
|
|
163
172
|
$: selectedIds, setCheckedValue();
|
|
173
|
+
$: activated = activatedId === row.id;
|
|
164
174
|
|
|
165
175
|
function setCheckedValue() {
|
|
166
176
|
checked = false;
|
|
@@ -175,7 +185,6 @@
|
|
|
175
185
|
|
|
176
186
|
function rowClick() {
|
|
177
187
|
if (clickableRows) {
|
|
178
|
-
rowActive = true;
|
|
179
188
|
fire("rowClick", { row });
|
|
180
189
|
}
|
|
181
190
|
}
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
@param {string} [iteration_key="id"] - Iteration key, Default: `"id"`
|
|
6
6
|
@param {array} [data=[]] - Contains all the results that needs to be displayed, Default: `[]`
|
|
7
7
|
@param {object} [columns={}] - Table column names. {db_field_name: column_name}, Default: `{}`
|
|
8
|
-
@param {boolean} [transition=false] - Determines if a
|
|
8
|
+
@param {boolean} [transition=false] - Determines if a transition effect is used, Default: `false`
|
|
9
9
|
@param {boolean} [is_striped=true] - Determines whether to use alternating row shading in the table view, Default: `true`
|
|
10
|
+
@param {boolean} [visualActivationOnClick=true] - Determines whether clickable rows activate visually on click, Default: `true`
|
|
10
11
|
@param {boolean} [clickableRows=false] - Determines whether rows are clickable or not, Default: `false`
|
|
12
|
+
@param {object} [activatedId=null] - Unique id of row that is activated, Default: `null`
|
|
11
13
|
@param {boolean} [bulk_actions=false] - Determines if selecting multiple rows and doing multiple actions is allowed, Default: `false`
|
|
12
14
|
@param {boolean} [selectAll=false] - Determines if all rows are selected, Default: `false`
|
|
13
15
|
@param {array} [selectedIds=[]] - List of unique IDs of all the selected rows, Default: `[]`
|
|
@@ -64,6 +66,7 @@
|
|
|
64
66
|
{transition}
|
|
65
67
|
{column_keys}
|
|
66
68
|
{clickableRows}
|
|
69
|
+
{visualActivationOnClick}
|
|
67
70
|
{isVisible}
|
|
68
71
|
{transforms}
|
|
69
72
|
{classNames}
|
|
@@ -71,6 +74,7 @@
|
|
|
71
74
|
{cellComponent}
|
|
72
75
|
{row}
|
|
73
76
|
{bulk_actions}
|
|
77
|
+
{activatedId}
|
|
74
78
|
{selectedIds}
|
|
75
79
|
{selectCheckboxColor}
|
|
76
80
|
{selectCheckboxSize}
|
|
@@ -100,7 +104,7 @@
|
|
|
100
104
|
*/
|
|
101
105
|
columns = {},
|
|
102
106
|
/**
|
|
103
|
-
* Determines if a
|
|
107
|
+
* Determines if a transition effect is used
|
|
104
108
|
*/
|
|
105
109
|
transition = false,
|
|
106
110
|
/**
|
|
@@ -108,10 +112,18 @@
|
|
|
108
112
|
* @link https://bulma.io/documentation/elements/table/#modifiers
|
|
109
113
|
*/
|
|
110
114
|
is_striped = true,
|
|
115
|
+
/**
|
|
116
|
+
* Determines whether clickable rows activate visually on click
|
|
117
|
+
*/
|
|
118
|
+
visualActivationOnClick = true,
|
|
111
119
|
/**
|
|
112
120
|
* Determines whether rows are clickable or not
|
|
113
121
|
*/
|
|
114
122
|
clickableRows = false,
|
|
123
|
+
/**
|
|
124
|
+
* Unique id of row that is activated
|
|
125
|
+
*/
|
|
126
|
+
activatedId = null,
|
|
115
127
|
/**
|
|
116
128
|
* Determines if selecting multiple rows and doing multiple actions is allowed
|
|
117
129
|
*/
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
@param {object} [tileItemComponent=null] - Contains a custom component, Default: `null`
|
|
9
9
|
@param {number} [per_row=3] - Sets how many items to display in a row, Default: `3`
|
|
10
10
|
@param {object} [columns={}] - Column names for the displayed table {db_field_name: column_name}, Default: `{}`
|
|
11
|
+
@param {boolean} [visualActivationOnClick=true] - Determines whether clickable rows activate visually on click, Default: `true`
|
|
11
12
|
@param {boolean} [clickableRows=false] - Determines whether rows are clickable or not, Default: `false`
|
|
13
|
+
@param {object} [activatedId=null] - Unique id of row that is activated, Default: `null`
|
|
12
14
|
@param {object} [valueTransformers={}] - Contains all custom values for each column, Default: `{}`
|
|
13
15
|
@param {object} [classTransformers={}] - CSS class names for each column, Default: `{}`
|
|
14
16
|
@param {object} [styleTransformers={}] - CSS styles for each column, Default: `{}`
|
|
@@ -31,6 +33,7 @@
|
|
|
31
33
|
on:_forwardEvent
|
|
32
34
|
{row_index}
|
|
33
35
|
{column_keys}
|
|
36
|
+
{visualActivationOnClick}
|
|
34
37
|
{clickableRows}
|
|
35
38
|
{isVisible}
|
|
36
39
|
{transforms}
|
|
@@ -38,6 +41,7 @@
|
|
|
38
41
|
{styles}
|
|
39
42
|
{row}
|
|
40
43
|
{bulk_actions}
|
|
44
|
+
{activatedId}
|
|
41
45
|
{selectedIds}
|
|
42
46
|
{selectCheckboxColor}
|
|
43
47
|
{selectCheckboxSize}
|
|
@@ -75,10 +79,18 @@
|
|
|
75
79
|
* Column names for the displayed table {db_field_name: column_name}
|
|
76
80
|
*/
|
|
77
81
|
columns = {},
|
|
82
|
+
/**
|
|
83
|
+
* Determines whether clickable rows activate visually on click
|
|
84
|
+
*/
|
|
85
|
+
visualActivationOnClick = true,
|
|
78
86
|
/**
|
|
79
87
|
* Determines whether rows are clickable or not
|
|
80
88
|
*/
|
|
81
89
|
clickableRows = false,
|
|
90
|
+
/**
|
|
91
|
+
* Unique id of row that is activated
|
|
92
|
+
*/
|
|
93
|
+
activatedId = null,
|
|
82
94
|
/**
|
|
83
95
|
* Contains all custom values for each column
|
|
84
96
|
*/
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
@param {object} [row={}] - List of all values in a row, Default: `{}`
|
|
6
|
-
@param {boolean} [
|
|
6
|
+
@param {boolean} [visualActivationOnClick=true] - Determines whether clickable rows activate visually on click, Default: `true`
|
|
7
|
+
@param {object} [activatedId=null] - Unique id of row that is activated, Default: `null`
|
|
7
8
|
@param {boolean} [clickableRows=false] - Determines whether the row is clickable or not, Default: `false`
|
|
8
9
|
@param {function} [isVisible()] - Returns whether a column can be visible or not
|
|
9
10
|
@param {function} [transforms()] - Returns column custom value
|
|
@@ -16,7 +17,7 @@
|
|
|
16
17
|
|
|
17
18
|
-->
|
|
18
19
|
<div
|
|
19
|
-
class:is-selected={
|
|
20
|
+
class:is-selected={activated && visualActivationOnClick}
|
|
20
21
|
class="box {clickableRows ? 'is-hoverable' : ''}"
|
|
21
22
|
on:click|stopPropagation={rowClick}>
|
|
22
23
|
{#each column_keys as column}
|
|
@@ -39,9 +40,13 @@
|
|
|
39
40
|
*/
|
|
40
41
|
export let row = {},
|
|
41
42
|
/**
|
|
42
|
-
* Determines whether
|
|
43
|
+
* Determines whether clickable rows activate visually on click
|
|
43
44
|
*/
|
|
44
|
-
|
|
45
|
+
visualActivationOnClick = true,
|
|
46
|
+
/**
|
|
47
|
+
* Unique id of row that is activated
|
|
48
|
+
*/
|
|
49
|
+
activatedId = null,
|
|
45
50
|
/**
|
|
46
51
|
* Determines whether the row is clickable or not
|
|
47
52
|
*/
|
|
@@ -67,9 +72,10 @@
|
|
|
67
72
|
*/
|
|
68
73
|
column_keys = [];
|
|
69
74
|
|
|
75
|
+
$: activated = activatedId === row.id;
|
|
76
|
+
|
|
70
77
|
function rowClick() {
|
|
71
78
|
if (clickableRows) {
|
|
72
|
-
rowActive = true;
|
|
73
79
|
/**
|
|
74
80
|
* Fires an event when a row is clicked
|
|
75
81
|
*/
|
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@param {string} [value=""] - Value of the Input
|
|
6
|
+
|
|
7
|
+
This property can be bound to, to fetch the current value, Default: `""`
|
|
8
|
+
@param {string} [placeholder=""] - Placeholder text for the input, Default: `""`
|
|
9
|
+
@param {array} [options=[]] - Array of strings, or objects.
|
|
10
|
+
Used to populate the list of options in the dropdown, Default: `[]`
|
|
11
|
+
@param {function|null} [search=null] - Async function to fetch options
|
|
12
|
+
|
|
13
|
+
Only send this prop if you want to fetch `options` asynchronously.
|
|
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.
|
|
16
|
+
Fuzzy match will not work if `search` function is set, as the backend service is meant to do the matching., Default: `"fuzzy"`
|
|
17
|
+
@param {boolean} [highlighted_results=true] - Whether to show the highlighted or plain results in the dropdown., Default: `true`
|
|
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: `""`
|
|
21
|
+
@param {string} [style=""] - Inline CSS for input container, Default: `""`
|
|
22
|
+
@param {boolean} [readonly=false] - Marks component as read-only, Default: `false`
|
|
23
|
+
@param {boolean} [disabled=false] - Disables the component, Default: `false`
|
|
24
|
+
@param {HTMLElement|string} [dropdown_portal=undefined] - Where to render the dropdown list.
|
|
25
|
+
Can be a DOM element or a `string` with the CSS selector of the element.
|
|
26
|
+
|
|
27
|
+
By default it renders in a custom container appended to `document.body`., Default: `undefined`
|
|
28
|
+
@param {string} [class=""] - CSS classes for input container, Default: `""`
|
|
29
|
+
|
|
30
|
+
### Events
|
|
31
|
+
- `change`
|
|
32
|
+
- `blur` - Triggered when the input loses focus
|
|
33
|
+
|
|
34
|
+
### Slots
|
|
35
|
+
- `<slot name="default" {option} />` - Slot containing text for each selectable item
|
|
36
|
+
|
|
37
|
+
Default value: `<span>{option.label}</span>`
|
|
38
|
+
|
|
39
|
+
-->
|
|
40
|
+
<div
|
|
41
|
+
bind:this={el}
|
|
42
|
+
class="
|
|
43
|
+
kws-autocomplete input
|
|
44
|
+
{disabled ? 'is-disabled' : ''}
|
|
45
|
+
{readonly ? 'is-readonly' : ''}
|
|
46
|
+
is-{size} is-{color} {klass}
|
|
47
|
+
"
|
|
48
|
+
class:readonly
|
|
49
|
+
{style}
|
|
50
|
+
on:click|stopPropagation={() => input && input.focus()}>
|
|
51
|
+
<input
|
|
52
|
+
class="input is-{size}"
|
|
53
|
+
bind:this={input}
|
|
54
|
+
autocomplete="off"
|
|
55
|
+
{disabled}
|
|
56
|
+
{readonly}
|
|
57
|
+
bind:value
|
|
58
|
+
on:keydown={handleKeydown}
|
|
59
|
+
on:blur={blurEvent}
|
|
60
|
+
on:blur={() => setOptionsVisible(false)}
|
|
61
|
+
{placeholder} />
|
|
62
|
+
{#if search && options_loading}
|
|
63
|
+
<button
|
|
64
|
+
type="button"
|
|
65
|
+
style="border: none;"
|
|
66
|
+
class="button is-paddingless delete is-medium is-loading" />
|
|
67
|
+
{/if}
|
|
68
|
+
{#if rootContainer}
|
|
69
|
+
<div class="kws-autocomplete" use:portal={dropdown_portal}>
|
|
70
|
+
<ul bind:this={dropdown} class="options" class:hidden={!show_options}>
|
|
71
|
+
{#each filtered_options as option}
|
|
72
|
+
<li
|
|
73
|
+
on:mousedown|preventDefault|stopPropagation={() =>
|
|
74
|
+
handleOptionMouseDown(option)}
|
|
75
|
+
on:mouseenter|preventDefault|stopPropagation={() => {
|
|
76
|
+
active_option = option;
|
|
77
|
+
}}
|
|
78
|
+
class="is-size-{list_text_size[size]}"
|
|
79
|
+
class:active={active_option === option}>
|
|
80
|
+
<!--
|
|
81
|
+
Slot containing text for each selectable item
|
|
82
|
+
|
|
83
|
+
Default value: `<span>{option.label}</span>`
|
|
84
|
+
-->
|
|
85
|
+
<slot {option}>
|
|
86
|
+
<!-- eslint-disable-next-line @ota-meshi/svelte/no-at-html-tags -->
|
|
87
|
+
{@html option.label}
|
|
88
|
+
</slot>
|
|
89
|
+
</li>
|
|
90
|
+
{/each}
|
|
91
|
+
</ul>
|
|
92
|
+
</div>
|
|
93
|
+
{/if}
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<script>
|
|
97
|
+
import { portal } from "@kws3/ui";
|
|
98
|
+
import { debounce } from "@kws3/ui/utils";
|
|
99
|
+
import { createEventDispatcher, onMount, tick } from "svelte";
|
|
100
|
+
import { createPopper } from "@popperjs/core";
|
|
101
|
+
import fuzzy from "fuzzy.js";
|
|
102
|
+
|
|
103
|
+
const sameWidthPopperModifier = {
|
|
104
|
+
name: "sameWidth",
|
|
105
|
+
enabled: true,
|
|
106
|
+
phase: "beforeWrite",
|
|
107
|
+
requires: ["computeStyles"],
|
|
108
|
+
fn: ({ state }) => {
|
|
109
|
+
state.styles.popper.width = `${Math.max(
|
|
110
|
+
200,
|
|
111
|
+
state.rects.reference.width
|
|
112
|
+
)}px`;
|
|
113
|
+
},
|
|
114
|
+
effect: ({ state }) => {
|
|
115
|
+
state.elements.popper.style.width = `${state.elements.reference.offsetWidth}px`;
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const rootContainerId = "kws-overlay-root";
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Value of the Input
|
|
123
|
+
*
|
|
124
|
+
* This property can be bound to, to fetch the current value
|
|
125
|
+
*/
|
|
126
|
+
export let value = "";
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Placeholder text for the input
|
|
130
|
+
*/
|
|
131
|
+
export let placeholder = "";
|
|
132
|
+
/**
|
|
133
|
+
* Array of strings, or objects.
|
|
134
|
+
* Used to populate the list of options in the dropdown
|
|
135
|
+
*/
|
|
136
|
+
export let options = [];
|
|
137
|
+
/**
|
|
138
|
+
* Async function to fetch options
|
|
139
|
+
*
|
|
140
|
+
* Only send this prop if you want to fetch `options` asynchronously.
|
|
141
|
+
* `options` prop will be ignored if this prop is set.
|
|
142
|
+
*
|
|
143
|
+
* @type {function|null}
|
|
144
|
+
*/
|
|
145
|
+
export let search = null;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Filtered options to be displayed strictly based on search text or perform a fuzzy match.
|
|
149
|
+
* Fuzzy match will not work if `search` function is set, as the backend service is meant to do the matching.
|
|
150
|
+
* @type {'fuzzy'|'strict'}
|
|
151
|
+
*/
|
|
152
|
+
export let search_strategy = "fuzzy";
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Whether to show the highlighted or plain results in the dropdown.
|
|
156
|
+
*/
|
|
157
|
+
export let highlighted_results = true;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Score threshold for fuzzy search strategy, setting high score gives more fuzzy matches.
|
|
161
|
+
*/
|
|
162
|
+
export let scoreThreshold = 5;
|
|
163
|
+
/**
|
|
164
|
+
* Size of the input
|
|
165
|
+
* @type {''|'small'|'medium'|'large'}
|
|
166
|
+
*/
|
|
167
|
+
export let size = "";
|
|
168
|
+
/**
|
|
169
|
+
* Color of the input
|
|
170
|
+
* @type {''|'primary'|'success'|'warning'|'info'|'danger'|'dark'|'light'}
|
|
171
|
+
*/
|
|
172
|
+
export let color = "";
|
|
173
|
+
/**
|
|
174
|
+
* Inline CSS for input container
|
|
175
|
+
*/
|
|
176
|
+
export let style = "";
|
|
177
|
+
/**
|
|
178
|
+
* Marks component as read-only
|
|
179
|
+
*/
|
|
180
|
+
export let readonly = false;
|
|
181
|
+
/**
|
|
182
|
+
* Disables the component
|
|
183
|
+
*/
|
|
184
|
+
export let disabled = false;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Where to render the dropdown list.
|
|
188
|
+
* Can be a DOM element or a `string` with the CSS selector of the element.
|
|
189
|
+
*
|
|
190
|
+
* By default it renders in a custom container appended to `document.body`.
|
|
191
|
+
*
|
|
192
|
+
* @type { HTMLElement|string}
|
|
193
|
+
*/
|
|
194
|
+
export let dropdown_portal = "#" + rootContainerId;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* CSS classes for input container
|
|
198
|
+
*/
|
|
199
|
+
let klass = "";
|
|
200
|
+
export { klass as class };
|
|
201
|
+
|
|
202
|
+
if (!search && (!options || !options.length))
|
|
203
|
+
console.error(`Missing options`);
|
|
204
|
+
|
|
205
|
+
//ensure we have a root container for all our hoisitng related stuff
|
|
206
|
+
|
|
207
|
+
let rootContainer = document.getElementById(rootContainerId);
|
|
208
|
+
if (!rootContainer) {
|
|
209
|
+
rootContainer = document.createElement("div");
|
|
210
|
+
rootContainer.id = rootContainerId;
|
|
211
|
+
document.body.appendChild(rootContainer);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const fire = createEventDispatcher();
|
|
215
|
+
|
|
216
|
+
let el, //whole wrapping element
|
|
217
|
+
dropdown, //dropdown ul
|
|
218
|
+
input, //the textbox to type in
|
|
219
|
+
POPPER,
|
|
220
|
+
active_option = "",
|
|
221
|
+
searching = true,
|
|
222
|
+
show_options = false,
|
|
223
|
+
filtered_options = [], //list of options filtered by search query
|
|
224
|
+
normalised_options = [], //list of options normalised
|
|
225
|
+
options_loading = false, //indictaes whether async search function is running
|
|
226
|
+
mounted = false; //indicates whether component is mounted
|
|
227
|
+
|
|
228
|
+
let list_text_size = {
|
|
229
|
+
small: "7",
|
|
230
|
+
medium: "5",
|
|
231
|
+
large: "4",
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
$: asyncMode = search && typeof search === "function";
|
|
235
|
+
|
|
236
|
+
$: options, normaliseOptions();
|
|
237
|
+
$: searching, updateFilteredOptions(value);
|
|
238
|
+
|
|
239
|
+
$: allow_fuzzy_match = !search && search_strategy === "fuzzy";
|
|
240
|
+
|
|
241
|
+
//convert arrays of strings into normalised arrays of objects
|
|
242
|
+
function normaliseOptions() {
|
|
243
|
+
let _items = options || [];
|
|
244
|
+
if (!_items || !(_items instanceof Array)) {
|
|
245
|
+
normalised_options = [];
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
normalised_options = normaliseArraysToObjects(_items);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function updateFilteredOptions(value) {
|
|
253
|
+
if (!mounted) return;
|
|
254
|
+
|
|
255
|
+
if (asyncMode) {
|
|
256
|
+
searching && debouncedTriggerSearch(sanitizeFilters(value));
|
|
257
|
+
} else {
|
|
258
|
+
searching && triggerSearch(sanitizeFilters(value));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function triggerSearch(filters) {
|
|
263
|
+
let cache = {};
|
|
264
|
+
//TODO - can optimize more for very long lists
|
|
265
|
+
filters.forEach((word, idx) => {
|
|
266
|
+
// iterate over each word in the search query
|
|
267
|
+
let opts = [];
|
|
268
|
+
if (word) {
|
|
269
|
+
if (allow_fuzzy_match) {
|
|
270
|
+
opts = fuzzySearch(word, normalised_options);
|
|
271
|
+
} else {
|
|
272
|
+
opts = [...normalised_options].filter((item) => {
|
|
273
|
+
// filter out items that don't match `filter`
|
|
274
|
+
if (typeof item === "object" && item.value) {
|
|
275
|
+
return (
|
|
276
|
+
typeof item.value === "string" &&
|
|
277
|
+
item.value.toLowerCase().indexOf(word) > -1
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
cache[idx] = opts; // storing options to current index on cache
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
filtered_options = Object.values(cache) // get values from cache
|
|
288
|
+
.flat() // flatten array
|
|
289
|
+
.filter((v, i, self) => i === self.findIndex((t) => t.value === v.value)); // remove duplicates
|
|
290
|
+
|
|
291
|
+
if (highlighted_results && !allow_fuzzy_match) {
|
|
292
|
+
filtered_options = highlightMatches(filtered_options, filters);
|
|
293
|
+
}
|
|
294
|
+
setOptionsVisible(true);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function triggerExternalSearch(filters) {
|
|
298
|
+
if (!filters.length) {
|
|
299
|
+
//do not trigger async search if filters are empty
|
|
300
|
+
clearDropDownResults();
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
options_loading = true;
|
|
304
|
+
// filtered_options = [];
|
|
305
|
+
search(filters).then((_options) => {
|
|
306
|
+
searching = false;
|
|
307
|
+
options_loading = false;
|
|
308
|
+
tick().then(() => {
|
|
309
|
+
filtered_options = normaliseArraysToObjects(_options);
|
|
310
|
+
|
|
311
|
+
if (highlighted_results) {
|
|
312
|
+
filtered_options = highlightMatches(filtered_options, filters);
|
|
313
|
+
}
|
|
314
|
+
setOptionsVisible(true);
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const debouncedTriggerSearch = debounce(triggerExternalSearch, 150, false);
|
|
320
|
+
|
|
321
|
+
onMount(() => {
|
|
322
|
+
POPPER = createPopper(el, dropdown, {
|
|
323
|
+
strategy: "fixed",
|
|
324
|
+
placement: "bottom-start",
|
|
325
|
+
modifiers: [sameWidthPopperModifier],
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
if (allow_fuzzy_match && fuzzy) {
|
|
329
|
+
fuzzy.analyzeSubTerms = true;
|
|
330
|
+
fuzzy.analyzeSubTermDepth = 10;
|
|
331
|
+
fuzzy.highlighting.before = "";
|
|
332
|
+
fuzzy.highlighting.after = "";
|
|
333
|
+
if (highlighted_results) {
|
|
334
|
+
fuzzy.highlighting.before = `<span class="h">`;
|
|
335
|
+
fuzzy.highlighting.after = "</span>";
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
//normalize value
|
|
340
|
+
if (value === null || typeof value == "undefined") {
|
|
341
|
+
value = null;
|
|
342
|
+
}
|
|
343
|
+
mounted = true;
|
|
344
|
+
|
|
345
|
+
return () => {
|
|
346
|
+
POPPER.destroy();
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
function setOptionsVisible(show) {
|
|
351
|
+
if (readonly || disabled || show === show_options) return;
|
|
352
|
+
if (!value || !filtered_options.length) {
|
|
353
|
+
show = false;
|
|
354
|
+
}
|
|
355
|
+
show_options = show;
|
|
356
|
+
if (!show) {
|
|
357
|
+
clearDropDownResults();
|
|
358
|
+
}
|
|
359
|
+
POPPER && POPPER.update();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function handleKeydown(event) {
|
|
363
|
+
if (event.key === `Enter`) {
|
|
364
|
+
show_options && event.preventDefault();
|
|
365
|
+
|
|
366
|
+
if (active_option) {
|
|
367
|
+
handleOptionMouseDown(active_option);
|
|
368
|
+
} else {
|
|
369
|
+
// no active option means no option is selected and the actual value should be what typed in input.
|
|
370
|
+
setOptionsVisible(false);
|
|
371
|
+
}
|
|
372
|
+
} else if ([`ArrowDown`, `ArrowUp`].includes(event.key)) {
|
|
373
|
+
show_options && event.preventDefault();
|
|
374
|
+
|
|
375
|
+
const increment = event.key === `ArrowUp` ? -1 : 1;
|
|
376
|
+
const newActiveIdx = filtered_options.indexOf(active_option) + increment;
|
|
377
|
+
|
|
378
|
+
if (newActiveIdx < 0) {
|
|
379
|
+
active_option = filtered_options[filtered_options.length - 1];
|
|
380
|
+
} else {
|
|
381
|
+
if (newActiveIdx === filtered_options.length)
|
|
382
|
+
active_option = filtered_options[0];
|
|
383
|
+
else active_option = filtered_options[newActiveIdx];
|
|
384
|
+
}
|
|
385
|
+
} else {
|
|
386
|
+
active_option = "";
|
|
387
|
+
searching = true;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function handleOptionMouseDown(option) {
|
|
392
|
+
add(option);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function add(token) {
|
|
396
|
+
if (readonly || disabled) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
value = token.value;
|
|
401
|
+
fire("change", { token, type: `add` });
|
|
402
|
+
|
|
403
|
+
setOptionsVisible(false);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function blurEvent() {
|
|
407
|
+
/**
|
|
408
|
+
* Triggered when the input loses focus
|
|
409
|
+
*/
|
|
410
|
+
fire("blur");
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const normaliseArraysToObjects = (arr) =>
|
|
414
|
+
[...arr].map((item) =>
|
|
415
|
+
typeof item === "object" ? item : { label: item, value: item }
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
const highlightMatches = (options, filters) => {
|
|
419
|
+
if (!filters.length) return options;
|
|
420
|
+
// join all filter parts and split into chars and filter out duplicates
|
|
421
|
+
let common_chars = [...filters.join("")].filter(
|
|
422
|
+
(v, i, self) => self.indexOf(v) === i
|
|
423
|
+
);
|
|
424
|
+
let pattern = new RegExp(
|
|
425
|
+
`[${common_chars.join("").replace(/\\/g, "\")}]`,
|
|
426
|
+
"gi"
|
|
427
|
+
);
|
|
428
|
+
return options.map((item) => {
|
|
429
|
+
return {
|
|
430
|
+
...item,
|
|
431
|
+
label: item.value.replace(
|
|
432
|
+
pattern,
|
|
433
|
+
(match) => `<span class="h">${match}</span>`
|
|
434
|
+
),
|
|
435
|
+
};
|
|
436
|
+
});
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const clearDropDownResults = () => {
|
|
440
|
+
filtered_options = [];
|
|
441
|
+
searching = false;
|
|
442
|
+
};
|
|
443
|
+
function sanitizeFilters(v) {
|
|
444
|
+
return v && v.trim() ? v.toLowerCase().trim().split(/\s+/) : [];
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function fuzzySearch(word, options) {
|
|
448
|
+
let OPTS = options.map((item) => {
|
|
449
|
+
let output = fuzzy(item.value, word);
|
|
450
|
+
item = { ...output, ...item };
|
|
451
|
+
item.label = output.highlightedTerm;
|
|
452
|
+
item.score =
|
|
453
|
+
!item.score || (item.score && item.score < output.score)
|
|
454
|
+
? output.score
|
|
455
|
+
: item.score || 0;
|
|
456
|
+
return item;
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
let maxScore = Math.max(...OPTS.map((i) => i.score));
|
|
460
|
+
let calculatedLimit = maxScore - scoreThreshold;
|
|
461
|
+
|
|
462
|
+
OPTS = OPTS.filter(
|
|
463
|
+
(r) => r.score > (calculatedLimit > 0 ? calculatedLimit : 0)
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
return OPTS;
|
|
467
|
+
}
|
|
468
|
+
</script>
|
|
@@ -22,6 +22,7 @@ Only send this prop if you want to fetch `options` asynchronously.
|
|
|
22
22
|
`options` prop will be ignored if this prop is set., Default: `null`
|
|
23
23
|
@param {'fuzzy'|'strict'} [search_strategy="fuzzy"] - Filtered options to be displayed strictly based on search text or perform a fuzzy match.
|
|
24
24
|
Fuzzy match will not work if `search` function is set, as the backend service is meant to do the matching., Default: `"fuzzy"`
|
|
25
|
+
@param {number} [scoreThreshold=3] - Score threshold for fuzzy search strategy, setting high score gives more fuzzy matches., Default: `3`
|
|
25
26
|
@param {''|'small'|'medium'|'large'} [size=""] - Size of the input, Default: `""`
|
|
26
27
|
@param {''|'primary'|'success'|'warning'|'info'|'danger'|'dark'|'light'} [color=""] - Color of the input, Default: `""`
|
|
27
28
|
@param {string} [style=""] - Inline CSS for input container, Default: `""`
|
|
@@ -131,6 +132,7 @@ Default value: `<span>{option[search_key] || option}</span>`
|
|
|
131
132
|
on:mouseenter|preventDefault|stopPropagation={() => {
|
|
132
133
|
activeOption = option;
|
|
133
134
|
}}
|
|
135
|
+
class="is-size-{list_text_size[size]}"
|
|
134
136
|
class:selected={isSelected(option)}
|
|
135
137
|
class:active={activeOption === option}>
|
|
136
138
|
<span class="kws-selected-icon"
|
|
@@ -145,7 +147,7 @@ Default value: `<span>{option[search_key] || option}</span>`
|
|
|
145
147
|
</li>
|
|
146
148
|
{:else}
|
|
147
149
|
{#if !options_loading}
|
|
148
|
-
<li class="no-options">
|
|
150
|
+
<li class="no-options is-size-{list_text_size[size]}">
|
|
149
151
|
{searchText ? no_options_msg : async_search_prompt}
|
|
150
152
|
</li>
|
|
151
153
|
{/if}
|
|
@@ -160,7 +162,7 @@ Default value: `<span>{option[search_key] || option}</span>`
|
|
|
160
162
|
import { debounce } from "@kws3/ui/utils";
|
|
161
163
|
import { createEventDispatcher, onMount, tick } from "svelte";
|
|
162
164
|
import { createPopper } from "@popperjs/core";
|
|
163
|
-
import
|
|
165
|
+
import fuzzy from "fuzzy.js";
|
|
164
166
|
|
|
165
167
|
const sameWidthPopperModifier = {
|
|
166
168
|
name: "sameWidth",
|
|
@@ -229,6 +231,11 @@ Default value: `<span>{option[search_key] || option}</span>`
|
|
|
229
231
|
* @type {'fuzzy'|'strict'}
|
|
230
232
|
*/
|
|
231
233
|
export let search_strategy = "fuzzy";
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Score threshold for fuzzy search strategy, setting high score gives more fuzzy matches.
|
|
237
|
+
*/
|
|
238
|
+
export let scoreThreshold = 3;
|
|
232
239
|
/**
|
|
233
240
|
* Size of the input
|
|
234
241
|
* @type {''|'small'|'medium'|'large'}
|
|
@@ -322,6 +329,12 @@ Default value: `<span>{option[search_key] || option}</span>`
|
|
|
322
329
|
selectedOptions = [], //list of options that are selected
|
|
323
330
|
options_loading = false; //indictaes whether async search function is running
|
|
324
331
|
|
|
332
|
+
let list_text_size = {
|
|
333
|
+
small: "7",
|
|
334
|
+
medium: "5",
|
|
335
|
+
large: "4",
|
|
336
|
+
};
|
|
337
|
+
|
|
325
338
|
$: single = max === 1;
|
|
326
339
|
$: asyncMode = search && typeof search === "function";
|
|
327
340
|
$: hasValue = single
|
|
@@ -350,11 +363,7 @@ Default value: `<span>{option[search_key] || option}</span>`
|
|
|
350
363
|
|
|
351
364
|
$: value, single, fillSelectedOptions();
|
|
352
365
|
|
|
353
|
-
$:
|
|
354
|
-
(activeOption && !filteredOptions.includes(activeOption)) ||
|
|
355
|
-
(!activeOption && searchText)
|
|
356
|
-
)
|
|
357
|
-
activeOption = filteredOptions[0];
|
|
366
|
+
$: activeOption, searchText, filteredOptions, updateActiveOption();
|
|
358
367
|
|
|
359
368
|
//TODO: optimise isSelected function
|
|
360
369
|
$: isSelected = (option) => {
|
|
@@ -395,23 +404,26 @@ Default value: `<span>{option[search_key] || option}</span>`
|
|
|
395
404
|
if (asyncMode && searching) {
|
|
396
405
|
debouncedTriggerSearch(filter);
|
|
397
406
|
} else {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
407
|
+
if (allow_fuzzy_match) {
|
|
408
|
+
filteredOptions = fuzzySearch(filter, [...normalisedOptions]);
|
|
409
|
+
} else {
|
|
410
|
+
filteredOptions = strictSearch(filter, [...normalisedOptions]);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function updateActiveOption() {
|
|
416
|
+
if (
|
|
417
|
+
(activeOption && searching && !filteredOptions.includes(activeOption)) ||
|
|
418
|
+
(!activeOption && searchText)
|
|
419
|
+
) {
|
|
420
|
+
activeOption = filteredOptions[0];
|
|
421
|
+
} else {
|
|
422
|
+
if (allow_fuzzy_match) {
|
|
423
|
+
activeOption = filteredOptions.find((opts) =>
|
|
424
|
+
matchesValue(activeOption, opts)
|
|
425
|
+
);
|
|
426
|
+
}
|
|
415
427
|
}
|
|
416
428
|
}
|
|
417
429
|
|
|
@@ -467,6 +479,11 @@ Default value: `<span>{option[search_key] || option}</span>`
|
|
|
467
479
|
modifiers: [sameWidthPopperModifier],
|
|
468
480
|
});
|
|
469
481
|
|
|
482
|
+
if (allow_fuzzy_match && fuzzy) {
|
|
483
|
+
fuzzy.analyzeSubTerms = true;
|
|
484
|
+
fuzzy.analyzeSubTermDepth = 10;
|
|
485
|
+
}
|
|
486
|
+
|
|
470
487
|
//normalize value for single versus multiselect
|
|
471
488
|
if (value === null || typeof value == "undefined") {
|
|
472
489
|
value = single ? null : [];
|
|
@@ -603,6 +620,7 @@ Default value: `<span>{option[search_key] || option}</span>`
|
|
|
603
620
|
setOptionsVisible(true);
|
|
604
621
|
}
|
|
605
622
|
} else if ([`ArrowDown`, `ArrowUp`].includes(event.key)) {
|
|
623
|
+
showOptions && event.preventDefault();
|
|
606
624
|
const increment = event.key === `ArrowUp` ? -1 : 1;
|
|
607
625
|
const newActiveIdx = filteredOptions.indexOf(activeOption) + increment;
|
|
608
626
|
|
|
@@ -666,16 +684,15 @@ Default value: `<span>{option[search_key] || option}</span>`
|
|
|
666
684
|
if (_value === null || typeof _value == "undefined") {
|
|
667
685
|
return false;
|
|
668
686
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
687
|
+
let value =
|
|
688
|
+
typeof _value === "object" && `${used_value_key}` in _value
|
|
689
|
+
? _value[used_value_key]
|
|
690
|
+
: _value;
|
|
691
|
+
return `${value}` === `${_option[used_value_key]}`;
|
|
672
692
|
};
|
|
673
693
|
|
|
674
694
|
const match = (needle, haystack) => {
|
|
675
|
-
|
|
676
|
-
return allow_fuzzy_match
|
|
677
|
-
? fuzzysearch(needle, _hayStack)
|
|
678
|
-
: _hayStack.indexOf(needle) > -1;
|
|
695
|
+
return haystack.toLowerCase().indexOf(needle) > -1;
|
|
679
696
|
};
|
|
680
697
|
|
|
681
698
|
const normaliseArraysToObjects = (arr) => {
|
|
@@ -696,4 +713,48 @@ Default value: `<span>{option[search_key] || option}</span>`
|
|
|
696
713
|
searching = false;
|
|
697
714
|
});
|
|
698
715
|
};
|
|
716
|
+
|
|
717
|
+
function fuzzySearch(filter, options) {
|
|
718
|
+
if (!filter) return options;
|
|
719
|
+
if (options.length) {
|
|
720
|
+
let OPTS = options.map((item) => {
|
|
721
|
+
let output = fuzzy(item[used_search_key], filter);
|
|
722
|
+
item = { ...output, original: item };
|
|
723
|
+
item.score =
|
|
724
|
+
!item.score || (item.score && item.score < output.score)
|
|
725
|
+
? output.score
|
|
726
|
+
: item.score || 0;
|
|
727
|
+
return item;
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
let maxScore = Math.max(...OPTS.map((i) => i.score));
|
|
731
|
+
let calculatedLimit = maxScore - scoreThreshold;
|
|
732
|
+
|
|
733
|
+
OPTS = OPTS.filter(
|
|
734
|
+
(r) => r.score > (calculatedLimit > 0 ? calculatedLimit : 0)
|
|
735
|
+
).map((o) => o.original);
|
|
736
|
+
|
|
737
|
+
return OPTS;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function strictSearch(filter, options) {
|
|
742
|
+
return options.filter((item) => {
|
|
743
|
+
// filter out items that don't match `filter`
|
|
744
|
+
if (typeof item === "object") {
|
|
745
|
+
if (used_search_key) {
|
|
746
|
+
return (
|
|
747
|
+
typeof item[used_search_key] === "string" &&
|
|
748
|
+
match(filter, item[used_search_key])
|
|
749
|
+
);
|
|
750
|
+
} else {
|
|
751
|
+
for (var key in item) {
|
|
752
|
+
return typeof item[key] === "string" && match(filter, item[key]);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
} else {
|
|
756
|
+
return match(filter, item);
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
}
|
|
699
760
|
</script>
|
package/index.js
CHANGED
|
@@ -49,6 +49,7 @@ export { default as Transition } from "./transitions/Transition.svelte";
|
|
|
49
49
|
export { default as SlidingPane } from "./sliding-panes/SlidingPane.svelte";
|
|
50
50
|
export { default as SlidingPaneSet } from "./sliding-panes/SlidingPaneSet.svelte";
|
|
51
51
|
|
|
52
|
+
export { default as AutoComplete } from "./forms/AutoComplete.svelte";
|
|
52
53
|
export { default as SearchableSelect } from "./forms/select/SearchableSelect.svelte";
|
|
53
54
|
export { default as MultiSelect } from "./forms/select/MultiSelect.svelte";
|
|
54
55
|
export { default as MaskedInput } from "./forms/MaskedInput.svelte";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kws3/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "UI components for use with Svelte v3 applications.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,9 +25,10 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"apexcharts": "3.33.2",
|
|
27
27
|
"flatpickr": "^4.5.2",
|
|
28
|
+
"fuzzy.js": "^0.1.0",
|
|
28
29
|
"svelte-portal": "^2.1.2",
|
|
29
30
|
"text-mask-core": "^5.1.2",
|
|
30
31
|
"tippy.js": "^6.3.1"
|
|
31
32
|
},
|
|
32
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "8862e5927943ef0610fe5f2f37630d977aff411f"
|
|
33
34
|
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
$kws-theme-colors: $colors !default;
|
|
2
|
+
$kws-autocomplete-radius: $radius !default;
|
|
3
|
+
$kws-autocomplete-border-color: $input-border-color !default;
|
|
4
|
+
$kws-autocomplete-box-shadow: 0 0.5em 1em -0.125em rgb(10 10 10 / 10%),
|
|
5
|
+
0 0px 0 1px rgb(10 10 10 / 2%) !default;
|
|
6
|
+
$kws-autocomplete-focus-border-color: $input-focus-border-color !default;
|
|
7
|
+
$kws-autocomplete-focus-box-shadow-size: $input-focus-box-shadow-size !default;
|
|
8
|
+
$kws-autocomplete-focus-box-shadow-color: $input-focus-box-shadow-color !default;
|
|
9
|
+
$kws-autocomplete-disabled-background-color: $input-disabled-background-color !default;
|
|
10
|
+
$kws-autocomplete-disabled-border-color: $input-disabled-border-color !default;
|
|
11
|
+
$kws-autocomplete-disabled-color: $input-disabled-color !default;
|
|
12
|
+
$kws-autocomplete-selecting-color: $primary-invert !default;
|
|
13
|
+
$kws-autocomplete-selecting-background: $primary !default;
|
|
14
|
+
$kws-autocomplete-text-matches-color: currentColor !default;
|
|
15
|
+
$kws-autocomplete-text-matches-background: transparent !default;
|
|
16
|
+
$kws-autocomplete-text-matches-font-weight: $weight-bold !default;
|
|
17
|
+
|
|
18
|
+
$__modal-z: 41 !default;
|
|
19
|
+
@if $modal-z {
|
|
20
|
+
$__modal-z: $modal-z;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
$kws-autocomplete-options-z-index: $__modal-z + 1 !default;
|
|
24
|
+
|
|
25
|
+
.kws-autocomplete {
|
|
26
|
+
position: relative;
|
|
27
|
+
align-items: center;
|
|
28
|
+
display: flex;
|
|
29
|
+
cursor: text;
|
|
30
|
+
height: auto;
|
|
31
|
+
min-height: 2.5em;
|
|
32
|
+
padding-top: calc(0.4em - 1px);
|
|
33
|
+
padding-bottom: calc(0.4em - 1px);
|
|
34
|
+
&:focus-within {
|
|
35
|
+
border-color: $kws-autocomplete-focus-border-color;
|
|
36
|
+
box-shadow: $kws-autocomplete-focus-box-shadow-size
|
|
37
|
+
$kws-autocomplete-focus-box-shadow-color;
|
|
38
|
+
}
|
|
39
|
+
&.is-disabled {
|
|
40
|
+
background-color: $kws-autocomplete-disabled-background-color;
|
|
41
|
+
border-color: $kws-autocomplete-disabled-border-color;
|
|
42
|
+
color: $kws-autocomplete-disabled-color;
|
|
43
|
+
cursor: not-allowed;
|
|
44
|
+
}
|
|
45
|
+
&.is-readonly {
|
|
46
|
+
box-shadow: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
input {
|
|
50
|
+
border: none !important;
|
|
51
|
+
outline: none !important;
|
|
52
|
+
background: none !important;
|
|
53
|
+
/* needed to hide red shadow around required inputs in some browsers */
|
|
54
|
+
box-shadow: none !important;
|
|
55
|
+
color: inherit;
|
|
56
|
+
flex: 1;
|
|
57
|
+
padding: 1pt;
|
|
58
|
+
height: auto;
|
|
59
|
+
min-height: 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
ul.options {
|
|
63
|
+
list-style: none;
|
|
64
|
+
max-height: 50vh;
|
|
65
|
+
padding: 0;
|
|
66
|
+
cursor: pointer;
|
|
67
|
+
overflow: auto;
|
|
68
|
+
background: #fff;
|
|
69
|
+
border: 1px solid $kws-autocomplete-border-color;
|
|
70
|
+
box-shadow: $kws-autocomplete-box-shadow;
|
|
71
|
+
position: relative;
|
|
72
|
+
z-index: 4;
|
|
73
|
+
&[data-popper-placement="top"] {
|
|
74
|
+
border-radius: $kws-autocomplete-radius $kws-autocomplete-radius 0 0;
|
|
75
|
+
box-shadow: 0 -1px 6px rgba(0, 0, 0, 0.4);
|
|
76
|
+
}
|
|
77
|
+
&.hidden {
|
|
78
|
+
display: none;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
li {
|
|
82
|
+
padding: 0.3em 0.5em;
|
|
83
|
+
position: relative;
|
|
84
|
+
word-break: break-all;
|
|
85
|
+
&.active {
|
|
86
|
+
// keyboard focused item
|
|
87
|
+
color: $kws-autocomplete-selecting-color;
|
|
88
|
+
background: $kws-autocomplete-selecting-background;
|
|
89
|
+
}
|
|
90
|
+
span.h {
|
|
91
|
+
// highlight text matches
|
|
92
|
+
font-weight: $kws-autocomplete-text-matches-font-weight;
|
|
93
|
+
color: $kws-autocomplete-text-matches-color;
|
|
94
|
+
background: $kws-autocomplete-text-matches-background;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@each $name, $pair in $kws-theme-colors {
|
|
101
|
+
$color: nth($pair, 1);
|
|
102
|
+
$color-invert: nth($pair, 2);
|
|
103
|
+
$color-light: findLightColor($color);
|
|
104
|
+
$color-dark: findDarkColor($color);
|
|
105
|
+
.kws-autocomplete {
|
|
106
|
+
&.is-#{$name} {
|
|
107
|
+
border-color: $color;
|
|
108
|
+
&:focus-within {
|
|
109
|
+
box-shadow: $input-focus-box-shadow-size bulmaRgba($color, 0.25);
|
|
110
|
+
}
|
|
111
|
+
ul.options {
|
|
112
|
+
li {
|
|
113
|
+
&.selected {
|
|
114
|
+
color: $color-dark;
|
|
115
|
+
background: $color-light;
|
|
116
|
+
}
|
|
117
|
+
&.active {
|
|
118
|
+
color: $color-invert;
|
|
119
|
+
background: $color;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
#kws-overlay-root {
|
|
128
|
+
.kws-autocomplete {
|
|
129
|
+
position: absolute;
|
|
130
|
+
z-index: $kws-autocomplete-options-z-index;
|
|
131
|
+
}
|
|
132
|
+
}
|
package/styles/Grid.scss
CHANGED
|
@@ -51,6 +51,7 @@ $kws-gridview-checked-row-background: $primary-light !default;
|
|
|
51
51
|
background-color: $kws-gridview-checked-row-background !important;
|
|
52
52
|
td {
|
|
53
53
|
background-color: $kws-gridview-checked-row-background !important;
|
|
54
|
+
color: findColorInvert($kws-gridview-checked-row-background) !important;
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
}
|