wx-svelte-grid 2.5.0 → 2.6.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/package.json +10 -10
- package/readme.md +20 -12
- package/src/components/Cell.svelte +3 -2
- package/src/components/Grid.svelte +7 -2
- package/src/components/HeaderCell.svelte +4 -3
- package/src/components/Layout.svelte +17 -15
- package/src/components/Tooltip.svelte +3 -2
- package/src/components/inlineEditors/Combo.svelte +5 -3
- package/src/components/inlineEditors/Datepicker.svelte +12 -5
- package/src/components/inlineEditors/Editor.svelte +10 -4
- package/src/components/inlineEditors/MultiSelect.svelte +113 -0
- package/src/components/inlineEditors/Richselect.svelte +6 -4
- package/src/components/inlineEditors/Text.svelte +5 -3
- package/src/components/inlineEditors/editors.js +6 -0
- package/src/components/inlineFilters/DatePicker.svelte +27 -0
- package/src/components/inlineFilters/filters.js +2 -0
- package/src/components/print/HeaderFooter.svelte +1 -1
- package/src/helpers/actions/reorder.js +1 -5
- package/src/index.js +2 -0
- package/types/index.d.ts +15 -0
- package/whatsnew.md +23 -0
- package/src/components/editor/Editor.svelte +0 -56
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wx-svelte-grid",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "A fast, feature-rich Svelte DataGrid component",
|
|
5
5
|
"productTag": "grid",
|
|
6
6
|
"productTrial": false,
|
|
@@ -33,19 +33,19 @@
|
|
|
33
33
|
},
|
|
34
34
|
"homepage": "https://svar.dev/svelte/datagrid/",
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@svar-ui/lib-dom": "0.
|
|
36
|
+
"@svar-ui/lib-dom": "0.12.1",
|
|
37
37
|
"@svar-ui/lib-state": "1.9.6",
|
|
38
38
|
"@svar-ui/lib-svelte": "0.5.2",
|
|
39
|
-
"@svar-ui/svelte-menu": "2.
|
|
40
|
-
"@svar-ui/svelte-core": "2.
|
|
41
|
-
"@svar-ui/svelte-toolbar": "2.
|
|
42
|
-
"@svar-ui/grid-data-provider": "2.
|
|
43
|
-
"@svar-ui/grid-locales": "2.
|
|
44
|
-
"@svar-ui/grid-store": "2.
|
|
39
|
+
"@svar-ui/svelte-menu": "2.5.0",
|
|
40
|
+
"@svar-ui/svelte-core": "2.5.0",
|
|
41
|
+
"@svar-ui/svelte-toolbar": "2.5.0",
|
|
42
|
+
"@svar-ui/grid-data-provider": "2.6.0",
|
|
43
|
+
"@svar-ui/grid-locales": "2.6.0",
|
|
44
|
+
"@svar-ui/grid-store": "2.6.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@svar-ui/svelte-editor": "2.
|
|
48
|
-
"@svar-ui/svelte-filter": "2.
|
|
47
|
+
"@svar-ui/svelte-editor": "2.5.0",
|
|
48
|
+
"@svar-ui/svelte-filter": "2.5.0"
|
|
49
49
|
},
|
|
50
50
|
"files": [
|
|
51
51
|
"src",
|
package/readme.md
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
</div>
|
|
21
21
|
|
|
22
|
-
[SVAR Svelte DataGrid](https://svar.dev/svelte/datagrid/) is a high-performance Svelte component for building feature-rich, accessible data tables
|
|
22
|
+
[SVAR Svelte DataGrid](https://svar.dev/svelte/datagrid/) is a high-performance Svelte component for building feature-rich, accessible data tables. It supports sorting, advanced filtering, paging, in-cell editing, and virtual scrolling out of the box. Comes with full TypeScript support and a flexible, developer-friendly API. Suitable for dashboards, admin panels, and data-heavy SaaS applications.
|
|
23
23
|
|
|
24
24
|
<div align="center">
|
|
25
25
|
<img src="https://cdn.svar.dev/public/react-grid.png" alt="SVAR Svelte DataGrid - Screenshot" width="700">
|
|
@@ -29,26 +29,30 @@
|
|
|
29
29
|
|
|
30
30
|
Here is a quick overview of what SVAR Svelte DataGrid offers:
|
|
31
31
|
|
|
32
|
-
- High performance (virtual scrolling
|
|
32
|
+
- High performance (virtual scrolling and dynamic loading)
|
|
33
33
|
- In-cell editing with different cell editors (datepicker, combo, select, rich select, etc.)
|
|
34
|
+
- External editor for grid data
|
|
34
35
|
- Custom HTML for cells
|
|
35
36
|
- Sorting by multiple columns
|
|
36
|
-
-
|
|
37
|
+
- Advanced filtering (including natural language)
|
|
37
38
|
- Paging
|
|
38
|
-
-
|
|
39
|
+
- Frozen columns
|
|
39
40
|
- Expandable/collapsible columns
|
|
41
|
+
- Row reordering with drag-and-drop
|
|
40
42
|
- Customizable tooltips for grid cells
|
|
41
43
|
- Context menu
|
|
42
|
-
-
|
|
44
|
+
- Built-in toolbar
|
|
43
45
|
- Tree-like structure
|
|
44
|
-
- Print support
|
|
45
|
-
-
|
|
46
|
-
- Accessibility: compatible with [WAI-ARIA](https://www.w3.org/WAI/standards-guidelines/aria/) standard
|
|
46
|
+
- Print support, export to CSV
|
|
47
|
+
- Undo/redo
|
|
47
48
|
- Keyboard navigation
|
|
49
|
+
- Accessibility: compatibility with [WAI-ARIA](https://www.w3.org/WAI/standards-guidelines/aria/) standard
|
|
48
50
|
- RestDataProvider for easy backend data binding
|
|
49
|
-
- Dark and light skins
|
|
51
|
+
- Dark and light skins, customizable with CSS (no Tailwind dependency)
|
|
50
52
|
- Full TypeScript support
|
|
51
53
|
|
|
54
|
+
[Check the demos](https://docs.svar.dev/svelte/grid/samples/#/base/willow) to see how these features work.
|
|
55
|
+
|
|
52
56
|
### :hammer_and_wrench: How to Use
|
|
53
57
|
|
|
54
58
|
To use SVAR Svelte DataGrid, simply import the package and include the component in your Svelte file:
|
|
@@ -87,14 +91,14 @@ To use SVAR Svelte DataGrid, simply import the package and include the component
|
|
|
87
91
|
|
|
88
92
|
For further instructions, see the detailed [how-to-start guide](https://docs.svar.dev/svelte/grid/getting_started).
|
|
89
93
|
|
|
90
|
-
###
|
|
94
|
+
### How to Modify
|
|
91
95
|
|
|
92
96
|
Typically, you don't need to modify the code. However, if you wish to do so, follow these steps:
|
|
93
97
|
|
|
94
98
|
1. Run `yarn` to install dependencies. Note that this project is a monorepo using `yarn` workspaces, so npm will not work
|
|
95
99
|
2. Start the project in development mode with `yarn start`
|
|
96
100
|
|
|
97
|
-
###
|
|
101
|
+
### Run Tests
|
|
98
102
|
|
|
99
103
|
To run the test:
|
|
100
104
|
|
|
@@ -107,6 +111,10 @@ To run the test:
|
|
|
107
111
|
yarn test:cypress
|
|
108
112
|
```
|
|
109
113
|
|
|
110
|
-
###
|
|
114
|
+
### Need Help?
|
|
111
115
|
|
|
112
116
|
Join our [community forum](https://forum.svar.dev) to get help or post feature requests.
|
|
117
|
+
|
|
118
|
+
### ⭐ Show Your Support
|
|
119
|
+
|
|
120
|
+
If SVAR Svelte DataGrid helps your project, [give us a star on GitHub](https://github.com/svar-widgets/grid)! It helps more developers discover this component and keeps our team motivated to ship new features.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { onDestroy, getContext, untrack } from "svelte";
|
|
3
3
|
import { getStyle } from "../helpers/columnWidth";
|
|
4
4
|
import { getRenderValue } from "@svar-ui/grid-store";
|
|
5
|
+
import { setID } from "@svar-ui/lib-dom";
|
|
5
6
|
|
|
6
7
|
let {
|
|
7
8
|
row,
|
|
@@ -95,8 +96,8 @@
|
|
|
95
96
|
onfocus={toggleFocusAction}
|
|
96
97
|
class:wx-fixed-right={column.fixed && column.fixed.right}
|
|
97
98
|
{style}
|
|
98
|
-
data-row-id={row.id}
|
|
99
|
-
data-col-id={column.id}
|
|
99
|
+
data-row-id={setID(row.id)}
|
|
100
|
+
data-col-id={setID(column.id)}
|
|
100
101
|
tabindex={focusable ? "0" : "-1"}
|
|
101
102
|
role={"gridcell"}
|
|
102
103
|
aria-colindex={column._colindex}
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
sortMarks = {},
|
|
40
40
|
undo = false,
|
|
41
41
|
hotkeys = null,
|
|
42
|
+
filterValues = {},
|
|
42
43
|
...restProps
|
|
43
44
|
} = $props();
|
|
44
45
|
|
|
@@ -147,7 +148,7 @@
|
|
|
147
148
|
let _skin = $derived(getContext("wx-theme"));
|
|
148
149
|
let init_once = true;
|
|
149
150
|
|
|
150
|
-
|
|
151
|
+
const reinitStore = () => {
|
|
151
152
|
dataStore.init({
|
|
152
153
|
data,
|
|
153
154
|
columns: finalColumns,
|
|
@@ -157,6 +158,7 @@
|
|
|
157
158
|
dynamic,
|
|
158
159
|
tree,
|
|
159
160
|
sortMarks,
|
|
161
|
+
filterValues,
|
|
160
162
|
select,
|
|
161
163
|
undo,
|
|
162
164
|
reorder,
|
|
@@ -167,7 +169,10 @@
|
|
|
167
169
|
init(api);
|
|
168
170
|
init_once = false;
|
|
169
171
|
}
|
|
170
|
-
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
reinitStore();
|
|
175
|
+
$effect(reinitStore);
|
|
171
176
|
</script>
|
|
172
177
|
|
|
173
178
|
<Locale words={en} optional={true}>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { resize } from "../helpers/actions/resize";
|
|
4
4
|
import { getCssName, getStyle } from "../helpers/columnWidth";
|
|
5
5
|
import Filter from "./inlineFilters/Filter.svelte";
|
|
6
|
+
import { setID } from "@svar-ui/lib-dom";
|
|
6
7
|
|
|
7
8
|
let {
|
|
8
9
|
cell,
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
}
|
|
50
51
|
api.exec("sort-rows", {
|
|
51
52
|
key: cell.id,
|
|
52
|
-
add: ev.ctrlKey,
|
|
53
|
+
add: ev.ctrlKey || ev.metaKey,
|
|
53
54
|
order: sortMark?.order,
|
|
54
55
|
});
|
|
55
56
|
}
|
|
@@ -106,7 +107,7 @@
|
|
|
106
107
|
tabindex="0"
|
|
107
108
|
onkeydown={toggleCollapseColumn}
|
|
108
109
|
onclick={collapse}
|
|
109
|
-
data-header-id={column.id}
|
|
110
|
+
data-header-id={setID(column.id)}
|
|
110
111
|
>
|
|
111
112
|
<div class="wx-text" style={collapsedTextStyle}>
|
|
112
113
|
{cell.text || ""}
|
|
@@ -121,7 +122,7 @@
|
|
|
121
122
|
class:wx-fixed-right={column.fixed && column.fixed.right}
|
|
122
123
|
{style}
|
|
123
124
|
onclick={sort}
|
|
124
|
-
data-header-id={column.id}
|
|
125
|
+
data-header-id={setID(column.id)}
|
|
125
126
|
tabindex={!cell._hidden && column.sort && !cell.filter
|
|
126
127
|
? "0"
|
|
127
128
|
: undefined}
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
import {
|
|
10
10
|
clickOutside,
|
|
11
11
|
delegateClick,
|
|
12
|
-
locateAttr,
|
|
13
12
|
locate,
|
|
14
|
-
|
|
13
|
+
setID,
|
|
14
|
+
locateID,
|
|
15
|
+
getID,
|
|
15
16
|
} from "@svar-ui/lib-dom";
|
|
16
17
|
|
|
17
18
|
import { hotkeys, defaultHotkeys } from "@svar-ui/grid-store";
|
|
@@ -410,12 +411,12 @@
|
|
|
410
411
|
|
|
411
412
|
const bodyClickHandlers = {
|
|
412
413
|
dblclick: (id, ev) => {
|
|
413
|
-
const data = { id, column:
|
|
414
|
+
const data = { id, column: locateID(ev, "data-col-id") };
|
|
414
415
|
api.exec("open-editor", data);
|
|
415
416
|
},
|
|
416
417
|
click: (id, ev) => {
|
|
417
418
|
if (postDrag) return;
|
|
418
|
-
const column =
|
|
419
|
+
const column = locateID(ev, "data-col-id");
|
|
419
420
|
if ($focusCell?.id !== id)
|
|
420
421
|
api.exec("focus-cell", {
|
|
421
422
|
row: id,
|
|
@@ -425,7 +426,7 @@
|
|
|
425
426
|
|
|
426
427
|
if ($select === false) return;
|
|
427
428
|
|
|
428
|
-
const toggle = multiselect && ev.ctrlKey;
|
|
429
|
+
const toggle = multiselect && (ev.ctrlKey || ev.metaKey);
|
|
429
430
|
const range = multiselect && ev.shiftKey;
|
|
430
431
|
|
|
431
432
|
if (
|
|
@@ -467,11 +468,11 @@
|
|
|
467
468
|
|
|
468
469
|
dragItem = from;
|
|
469
470
|
|
|
470
|
-
if (api.getRow(dragItem).open)
|
|
471
|
+
if ($tree && api.getRow(dragItem).open)
|
|
471
472
|
api.exec("close-row", { id: dragItem, nested: true });
|
|
472
473
|
|
|
473
474
|
// default to drag source (target may be shifted by this moment)
|
|
474
|
-
const itemNode = locate(sourceNode
|
|
475
|
+
const itemNode = locate(sourceNode);
|
|
475
476
|
dragNode = itemNode.cloneNode(true);
|
|
476
477
|
dragNode.classList.remove("wx-selected");
|
|
477
478
|
dragNode
|
|
@@ -521,8 +522,8 @@
|
|
|
521
522
|
}
|
|
522
523
|
|
|
523
524
|
if (tableNode.contains(context.targetNode)) {
|
|
524
|
-
const targetRow = locate(context.targetNode
|
|
525
|
-
const to =
|
|
525
|
+
const targetRow = locate(context.targetNode);
|
|
526
|
+
const to = targetRow && getID(targetRow);
|
|
526
527
|
|
|
527
528
|
if (to && to !== from) {
|
|
528
529
|
context.to = to;
|
|
@@ -675,9 +676,9 @@
|
|
|
675
676
|
!focus ||
|
|
676
677
|
(visibleSelection.length &&
|
|
677
678
|
!visibleSelection.includes(focus.row)) ||
|
|
678
|
-
dataRows.findIndex(r => r.id
|
|
679
|
+
dataRows.findIndex(r => r.id === focus.row) === -1 ||
|
|
679
680
|
renderColumns.data.findIndex(
|
|
680
|
-
c => c.id
|
|
681
|
+
c => c.id === focus.column && !c.collapsed
|
|
681
682
|
) === -1
|
|
682
683
|
) {
|
|
683
684
|
const row = visibleSelection[0] || dataRows[0].id;
|
|
@@ -753,6 +754,7 @@
|
|
|
753
754
|
style="width:{contentWidth}px;height:{fullHeight}px;"
|
|
754
755
|
onmousedown={ev => lockSelection(ev)}
|
|
755
756
|
use:clickOutside={() =>
|
|
757
|
+
$focusCell &&
|
|
756
758
|
api.exec("focus-cell", { eventSource: "click" })}
|
|
757
759
|
use:delegateClick={bodyClickHandlers}
|
|
758
760
|
>
|
|
@@ -771,8 +773,8 @@
|
|
|
771
773
|
class:wx-autoheight={autoRowHeight}
|
|
772
774
|
class={"wx-row" +
|
|
773
775
|
(rowStyle ? " " + rowStyle(row) : "")}
|
|
774
|
-
data-id={row.id}
|
|
775
|
-
data-context-id={row.id}
|
|
776
|
+
data-id={setID(row.id)}
|
|
777
|
+
data-context-id={setID(row.id)}
|
|
776
778
|
class:wx-selected={isSelected}
|
|
777
779
|
class:wx-inactive={dragItem === row.id}
|
|
778
780
|
style={`${autoRowHeight ? "min-height" : "height"}:${row.rowHeight || defaultRowHeight}px;`}
|
|
@@ -786,7 +788,7 @@
|
|
|
786
788
|
{#each renderColumns.data as column (column.id)}
|
|
787
789
|
{#if column.collapsed}
|
|
788
790
|
<div class="wx-cell wx-collapsed"></div>
|
|
789
|
-
{:else if $editor?.id === row.id && $editor.column
|
|
791
|
+
{:else if $editor?.id === row.id && $editor.column === column.id}
|
|
790
792
|
<Editor {row} {column} />
|
|
791
793
|
{:else}
|
|
792
794
|
<Cell
|
|
@@ -796,7 +798,7 @@
|
|
|
796
798
|
{cellStyle}
|
|
797
799
|
{reorder}
|
|
798
800
|
focusable={focus?.row === row.id &&
|
|
799
|
-
focus?.column
|
|
801
|
+
focus?.column === column.id}
|
|
800
802
|
/>
|
|
801
803
|
{/if}
|
|
802
804
|
{/each}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { getRenderValue } from "@svar-ui/grid-store";
|
|
3
|
+
import { getID } from "@svar-ui/lib-dom";
|
|
3
4
|
|
|
4
5
|
let { content: Content = null, api, children } = $props();
|
|
5
6
|
|
|
@@ -12,8 +13,8 @@
|
|
|
12
13
|
function findAttribute(node) {
|
|
13
14
|
while (node) {
|
|
14
15
|
if (node.getAttribute) {
|
|
15
|
-
const id = node
|
|
16
|
-
const colId = node
|
|
16
|
+
const id = getID(node, "data-row-id");
|
|
17
|
+
const colId = getID(node, "data-col-id");
|
|
17
18
|
if (id && api && colId) {
|
|
18
19
|
const col = api.getColumn(colId);
|
|
19
20
|
return { id, col, target: node };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { onMount } from "svelte";
|
|
3
3
|
import { SuggestDropdown } from "@svar-ui/svelte-core";
|
|
4
|
+
import { clickOutside } from "@svar-ui/lib-dom";
|
|
4
5
|
|
|
5
|
-
let {
|
|
6
|
+
let { editor, onaction, onsave, onapply } = $props();
|
|
6
7
|
|
|
7
8
|
let { value, renderedValue: text, options: filterOptions } = $state(editor);
|
|
8
9
|
let { template, cell } = $state(editor?.config || {});
|
|
@@ -10,8 +11,8 @@
|
|
|
10
11
|
let index = $derived(filterOptions.findIndex(a => a.id === value));
|
|
11
12
|
|
|
12
13
|
function updateValue({ id }) {
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
onapply(id);
|
|
15
|
+
onsave();
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
let navigate;
|
|
@@ -46,6 +47,7 @@
|
|
|
46
47
|
bind:value={text}
|
|
47
48
|
oninput={input}
|
|
48
49
|
onkeydown={e => keydown(e, index)}
|
|
50
|
+
use:clickOutside={() => onsave(true)}
|
|
49
51
|
/>
|
|
50
52
|
<SuggestDropdown items={filterOptions} onready={ready} onselect={updateValue}>
|
|
51
53
|
{#snippet children({ option })}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { onMount } from "svelte";
|
|
3
|
+
import { clickOutside } from "@svar-ui/lib-dom";
|
|
3
4
|
|
|
4
5
|
import { Calendar, Dropdown } from "@svar-ui/svelte-core";
|
|
5
6
|
|
|
6
|
-
let {
|
|
7
|
+
let { editor, onaction, onsave, onapply, oncancel } = $props();
|
|
7
8
|
|
|
8
9
|
let value = $state(editor.value || new Date());
|
|
9
10
|
|
|
@@ -11,8 +12,8 @@
|
|
|
11
12
|
let cell = $state(editor.config?.cell);
|
|
12
13
|
|
|
13
14
|
function updateValue({ value }) {
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
onapply(value);
|
|
16
|
+
onsave();
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
let node;
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
class="wx-value"
|
|
32
33
|
bind:this={node}
|
|
33
34
|
tabindex="0"
|
|
34
|
-
onclick={
|
|
35
|
+
onclick={oncancel}
|
|
35
36
|
onkeydown={ev => ev.preventDefault()}
|
|
36
37
|
>
|
|
37
38
|
{#if template}
|
|
@@ -42,7 +43,13 @@
|
|
|
42
43
|
{:else}<span class="wx-text">{editor.renderedValue}</span>{/if}
|
|
43
44
|
</div>
|
|
44
45
|
<Dropdown width={"auto"}>
|
|
45
|
-
<
|
|
46
|
+
<div use:clickOutside={() => onsave(true)}>
|
|
47
|
+
<Calendar
|
|
48
|
+
{value}
|
|
49
|
+
onchange={updateValue}
|
|
50
|
+
buttons={editor.config?.buttons}
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
46
53
|
</Dropdown>
|
|
47
54
|
|
|
48
55
|
<style>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { getContext } from "svelte";
|
|
3
3
|
import { getStyle } from "../../helpers/columnWidth";
|
|
4
|
-
import { clickOutside } from "@svar-ui/lib-dom";
|
|
5
4
|
import { editors } from "./editors";
|
|
6
5
|
|
|
7
6
|
let { column, row } = $props();
|
|
@@ -35,7 +34,13 @@
|
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
function keyHandler(ev) {
|
|
38
|
-
if (ev.key === "Enter" && $editor)
|
|
37
|
+
if (ev.key === "Enter" && $editor) {
|
|
38
|
+
if (column.editor.type === "multiselect") {
|
|
39
|
+
updateValue($editor.value);
|
|
40
|
+
} else {
|
|
41
|
+
cancel();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
let style = $derived(
|
|
@@ -71,11 +76,12 @@
|
|
|
71
76
|
onclick={ev => ev.stopPropagation()}
|
|
72
77
|
ondblclick={ev => ev.stopPropagation()}
|
|
73
78
|
onkeydown={keyHandler}
|
|
74
|
-
use:clickOutside={() => save(true)}
|
|
75
79
|
>
|
|
76
80
|
<SvelteComponent
|
|
77
81
|
editor={$editor}
|
|
78
|
-
|
|
82
|
+
onsave={save}
|
|
83
|
+
onapply={updateValue}
|
|
84
|
+
oncancel={cancel}
|
|
79
85
|
onaction={({ action, data }) => api.exec(action, data)}
|
|
80
86
|
/>
|
|
81
87
|
</div>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { SuggestDropdown } from "@svar-ui/svelte-core";
|
|
3
|
+
import { clickOutside } from "@svar-ui/lib-dom";
|
|
4
|
+
import { onMount } from "svelte";
|
|
5
|
+
|
|
6
|
+
let { editor, onaction, onsave, onapply, oncancel } = $props();
|
|
7
|
+
let { config } = $state(editor);
|
|
8
|
+
|
|
9
|
+
const options = $derived(editor?.options ?? []);
|
|
10
|
+
let value = $derived(editor?.value || []);
|
|
11
|
+
let renderedValue = $derived(editor?.renderedValue);
|
|
12
|
+
let index = $derived.by(() => {
|
|
13
|
+
const firstSelected = options.find(opt => value.includes(opt.id));
|
|
14
|
+
return firstSelected ? options.indexOf(firstSelected) : -1;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function updateValue({ id }) {
|
|
18
|
+
onapply(id);
|
|
19
|
+
node.focus();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let navigate;
|
|
23
|
+
let keydown = $state();
|
|
24
|
+
|
|
25
|
+
function ready(ev) {
|
|
26
|
+
navigate = ev.navigate;
|
|
27
|
+
keydown = ev.keydown;
|
|
28
|
+
navigate(index);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let node = $state();
|
|
32
|
+
onMount(() => {
|
|
33
|
+
node.focus();
|
|
34
|
+
if (window && window.getSelection) {
|
|
35
|
+
window.getSelection().removeAllRanges();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
41
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
42
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
43
|
+
<div
|
|
44
|
+
bind:this={node}
|
|
45
|
+
class="wx-value"
|
|
46
|
+
tabindex="0"
|
|
47
|
+
onclick={oncancel}
|
|
48
|
+
onkeydown={ev => {
|
|
49
|
+
keydown(ev, index);
|
|
50
|
+
ev.preventDefault();
|
|
51
|
+
}}
|
|
52
|
+
use:clickOutside={() => onsave(true)}
|
|
53
|
+
>
|
|
54
|
+
{#if config?.template}
|
|
55
|
+
{config.template(value?.map(id => options.find(opt => opt.id === id)))}
|
|
56
|
+
{:else if config?.cell}
|
|
57
|
+
{@const SvelteComponent = config.cell}
|
|
58
|
+
<SvelteComponent
|
|
59
|
+
data={value.map(id => options.find(opt => opt.id === id))}
|
|
60
|
+
/>
|
|
61
|
+
{:else}
|
|
62
|
+
<span class="wx-text">{renderedValue}</span>
|
|
63
|
+
{/if}
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<SuggestDropdown
|
|
67
|
+
items={options}
|
|
68
|
+
onready={ready}
|
|
69
|
+
onselect={updateValue}
|
|
70
|
+
checkboxes={true}
|
|
71
|
+
multiselect={true}
|
|
72
|
+
{value}
|
|
73
|
+
>
|
|
74
|
+
{#snippet children({ option })}
|
|
75
|
+
<div class="wx-option">
|
|
76
|
+
{#if config?.template}
|
|
77
|
+
{config.template(option)}
|
|
78
|
+
{:else if config?.cell}
|
|
79
|
+
{@const SvelteComponent = config.cell}
|
|
80
|
+
<SvelteComponent data={option} {onaction} />
|
|
81
|
+
{:else}
|
|
82
|
+
{option.label}
|
|
83
|
+
{/if}
|
|
84
|
+
</div>
|
|
85
|
+
{/snippet}
|
|
86
|
+
</SuggestDropdown>
|
|
87
|
+
|
|
88
|
+
<style>
|
|
89
|
+
.wx-option {
|
|
90
|
+
display: flex;
|
|
91
|
+
direction: row;
|
|
92
|
+
align-items: center;
|
|
93
|
+
justify-content: flex-start;
|
|
94
|
+
gap: 8px;
|
|
95
|
+
}
|
|
96
|
+
.wx-text {
|
|
97
|
+
width: 100%;
|
|
98
|
+
white-space: nowrap;
|
|
99
|
+
overflow: hidden;
|
|
100
|
+
text-overflow: ellipsis;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.wx-value {
|
|
104
|
+
width: 100%;
|
|
105
|
+
height: 100%;
|
|
106
|
+
padding: 8px;
|
|
107
|
+
overflow: hidden;
|
|
108
|
+
outline: none;
|
|
109
|
+
border: 1px solid var(--wx-color-primary);
|
|
110
|
+
text-overflow: ellipsis;
|
|
111
|
+
white-space: nowrap;
|
|
112
|
+
}
|
|
113
|
+
</style>
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { onMount } from "svelte";
|
|
3
3
|
import { SuggestDropdown } from "@svar-ui/svelte-core";
|
|
4
|
+
import { clickOutside } from "@svar-ui/lib-dom";
|
|
4
5
|
|
|
5
|
-
let {
|
|
6
|
+
let { editor, onaction, onsave, onapply, oncancel } = $props();
|
|
6
7
|
|
|
7
8
|
let data = $state(editor.options.find(opt => opt.id === editor.value));
|
|
8
9
|
let { value, options } = $state(editor);
|
|
@@ -11,8 +12,8 @@
|
|
|
11
12
|
let index = $derived(options.findIndex(a => a.id === value));
|
|
12
13
|
|
|
13
14
|
function updateValue({ id }) {
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
onapply(id);
|
|
16
|
+
onsave();
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
let navigate;
|
|
@@ -40,11 +41,12 @@
|
|
|
40
41
|
bind:this={node}
|
|
41
42
|
class="wx-value"
|
|
42
43
|
tabindex="0"
|
|
43
|
-
onclick={
|
|
44
|
+
onclick={oncancel}
|
|
44
45
|
onkeydown={ev => {
|
|
45
46
|
keydown(ev, index);
|
|
46
47
|
ev.preventDefault();
|
|
47
48
|
}}
|
|
49
|
+
use:clickOutside={() => onsave(true)}
|
|
48
50
|
>
|
|
49
51
|
{#if template}
|
|
50
52
|
{template(data)}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { onMount } from "svelte";
|
|
3
|
+
import { clickOutside } from "@svar-ui/lib-dom";
|
|
3
4
|
|
|
4
|
-
let {
|
|
5
|
+
let { editor, onsave, onapply } = $props();
|
|
5
6
|
|
|
6
7
|
let value = $state(editor.value || "");
|
|
7
8
|
|
|
@@ -10,15 +11,16 @@
|
|
|
10
11
|
|
|
11
12
|
function updateValue() {
|
|
12
13
|
value = node.value;
|
|
13
|
-
|
|
14
|
+
onapply(node.value);
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
function closeAndSave({ key }) {
|
|
17
|
-
if (key === "Enter")
|
|
18
|
+
if (key === "Enter") onsave();
|
|
18
19
|
}
|
|
19
20
|
</script>
|
|
20
21
|
|
|
21
22
|
<input
|
|
23
|
+
use:clickOutside={() => onsave(true)}
|
|
22
24
|
class="wx-text"
|
|
23
25
|
oninput={updateValue}
|
|
24
26
|
onkeydown={closeAndSave}
|
|
@@ -2,10 +2,16 @@ import Text from "./Text.svelte";
|
|
|
2
2
|
import Combo from "./Combo.svelte";
|
|
3
3
|
import Datepicker from "./Datepicker.svelte";
|
|
4
4
|
import Richselect from "./Richselect.svelte";
|
|
5
|
+
import Multiselect from "./MultiSelect.svelte";
|
|
5
6
|
|
|
6
7
|
export const editors = {
|
|
7
8
|
text: Text,
|
|
8
9
|
combo: Combo,
|
|
9
10
|
datepicker: Datepicker,
|
|
10
11
|
richselect: Richselect,
|
|
12
|
+
multiselect: Multiselect,
|
|
11
13
|
};
|
|
14
|
+
|
|
15
|
+
export function registerInlineEditor(type, component) {
|
|
16
|
+
editors[type] = component;
|
|
17
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { DatePicker } from "@svar-ui/svelte-core";
|
|
3
|
+
|
|
4
|
+
let { filter, column, action, filterValue } = $props();
|
|
5
|
+
|
|
6
|
+
function filterRows({ value }) {
|
|
7
|
+
action({ value, key: column.id });
|
|
8
|
+
}
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
12
|
+
<div style="width:100%;">
|
|
13
|
+
<DatePicker
|
|
14
|
+
placeholder={""}
|
|
15
|
+
clear={true}
|
|
16
|
+
{...filter.config ?? {}}
|
|
17
|
+
value={filterValue}
|
|
18
|
+
onchange={filterRows}
|
|
19
|
+
/>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<style>
|
|
23
|
+
:global(.wx-cell.wx-filter div.wx-datepicker input) {
|
|
24
|
+
height: 28px;
|
|
25
|
+
padding: 4px 8px;
|
|
26
|
+
}
|
|
27
|
+
</style>
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
{#each columns as row, i}
|
|
16
16
|
<tr>
|
|
17
17
|
{#each row as cell (cell.id)}
|
|
18
|
-
{@const column = _columns.find(c => c.id
|
|
18
|
+
{@const column = _columns.find(c => c.id === cell.id)}
|
|
19
19
|
<th
|
|
20
20
|
style={getPrintCellStyle(cell, sizes.columnWidth)}
|
|
21
21
|
class="wx-print-cell-{type} {getColumnCss(column)}"
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import { locate,
|
|
1
|
+
import { locate, getID } from "@svar-ui/lib-dom";
|
|
2
2
|
|
|
3
3
|
const SHIFT = 5;
|
|
4
4
|
const LONG_TOUCH_DELAY = 700;
|
|
5
5
|
|
|
6
|
-
function getID(node) {
|
|
7
|
-
return id(node.getAttribute("data-id"));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
6
|
export function getOffset(node) {
|
|
11
7
|
const box = node.getBoundingClientRect();
|
|
12
8
|
const body = document.body;
|
package/src/index.js
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
TMethodsConfig,
|
|
11
11
|
IConfig,
|
|
12
12
|
TEditorType,
|
|
13
|
+
TEditorConfig,
|
|
13
14
|
IColumnEditor,
|
|
14
15
|
IHeaderCell,
|
|
15
16
|
} from "@svar-ui/grid-store";
|
|
@@ -131,6 +132,20 @@ export declare const WillowDark: Component<{
|
|
|
131
132
|
children?: () => any;
|
|
132
133
|
}>;
|
|
133
134
|
|
|
135
|
+
export declare function registerInlineEditor(
|
|
136
|
+
type: string,
|
|
137
|
+
component: Component<{
|
|
138
|
+
editor: TEditorConfig;
|
|
139
|
+
onsave?: (ignoreFocus: boolean) => void;
|
|
140
|
+
oncancel?: () => void;
|
|
141
|
+
onapply?: (value: any) => void;
|
|
142
|
+
onaction?: (ev: {
|
|
143
|
+
action: string;
|
|
144
|
+
data?: { [key: string]: any };
|
|
145
|
+
}) => void;
|
|
146
|
+
}>
|
|
147
|
+
): void;
|
|
148
|
+
|
|
134
149
|
/* get component events from store actions*/
|
|
135
150
|
type RemoveHyphen<S extends string> = S extends `${infer Head}-${infer Tail}`
|
|
136
151
|
? `${Head}${RemoveHyphen<Tail>}`
|
package/whatsnew.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
## 2.6.0
|
|
2
|
+
|
|
3
|
+
### New features
|
|
4
|
+
|
|
5
|
+
- Multiselect inline editor
|
|
6
|
+
- Ability to register custom inline editors
|
|
7
|
+
|
|
8
|
+
### Updates
|
|
9
|
+
|
|
10
|
+
- Integration with FilterQuery
|
|
11
|
+
|
|
12
|
+
### Fixes
|
|
13
|
+
|
|
14
|
+
- Dropdown editors are cut off in small tables
|
|
15
|
+
- DataGrid fails to initialize in SvelteKit with serverside rendering
|
|
16
|
+
|
|
17
|
+
## 2.5.1
|
|
18
|
+
|
|
19
|
+
### Fixes
|
|
20
|
+
|
|
21
|
+
- Items with string ids fail in some operations
|
|
22
|
+
- Multiple sorting and selection does not work on MacOs
|
|
23
|
+
|
|
1
24
|
## 2.5.0
|
|
2
25
|
|
|
3
26
|
### New features
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import {
|
|
3
|
-
Field,
|
|
4
|
-
Text,
|
|
5
|
-
RichSelect,
|
|
6
|
-
MultiCombo,
|
|
7
|
-
DatePicker,
|
|
8
|
-
Area,
|
|
9
|
-
Checkbox,
|
|
10
|
-
Switch,
|
|
11
|
-
} from "@svar-ui/svelte-core";
|
|
12
|
-
|
|
13
|
-
let { editors, data = null } = $props();
|
|
14
|
-
</script>
|
|
15
|
-
|
|
16
|
-
{#if data}
|
|
17
|
-
{#each editors as editor}
|
|
18
|
-
{#if editor.type === "combo"}
|
|
19
|
-
<Field label={editor.label}>
|
|
20
|
-
<RichSelect
|
|
21
|
-
options={editor.options || []}
|
|
22
|
-
bind:value={$data[editor.id]}
|
|
23
|
-
/>
|
|
24
|
-
</Field>
|
|
25
|
-
{:else if editor.type === "multicombo"}
|
|
26
|
-
<Field label={editor.label}>
|
|
27
|
-
<MultiCombo
|
|
28
|
-
textField="name"
|
|
29
|
-
checkboxes={false}
|
|
30
|
-
options={editor.options || []}
|
|
31
|
-
bind:value={$data[editor.id]}
|
|
32
|
-
/>
|
|
33
|
-
</Field>
|
|
34
|
-
{:else if editor.type === "datepicker"}
|
|
35
|
-
<Field label={editor.label}>
|
|
36
|
-
<DatePicker bind:value={$data[editor.id]} />
|
|
37
|
-
</Field>
|
|
38
|
-
{:else if editor.type === "textarea"}
|
|
39
|
-
<Field label={editor.label}>
|
|
40
|
-
<Area bind:value={$data[editor.id]} />
|
|
41
|
-
</Field>
|
|
42
|
-
{:else if editor.type === "switch"}
|
|
43
|
-
<Field label={editor.label}>
|
|
44
|
-
<Switch bind:value={$data[editor.id]} />
|
|
45
|
-
</Field>
|
|
46
|
-
{:else if editor.type === "checkbox"}
|
|
47
|
-
<Field label={editor.label}>
|
|
48
|
-
<Checkbox bind:value={$data[editor.id]} />
|
|
49
|
-
</Field>
|
|
50
|
-
{:else}
|
|
51
|
-
<Field label={editor.label}>
|
|
52
|
-
<Text bind:value={$data[editor.id]} />
|
|
53
|
-
</Field>
|
|
54
|
-
{/if}
|
|
55
|
-
{/each}
|
|
56
|
-
{/if}
|