tosijs-ui 1.0.0 → 1.0.2
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/README.md +4 -2
- package/dist/iife.js +70 -60
- package/dist/iife.js.map +42 -42
- package/dist/index.d.ts +1 -1
- package/dist/index.js +15 -37
- package/dist/index.js.map +39 -39
- package/dist/version.d.ts +1 -1
- package/package.json +2 -2
- package/dist/ab-test.js +0 -116
- package/dist/babylon-3d.js +0 -292
- package/dist/bodymovin-player.js +0 -172
- package/dist/bp-loader.js +0 -26
- package/dist/carousel.js +0 -308
- package/dist/code-editor.js +0 -102
- package/dist/color-input.js +0 -112
- package/dist/data-table.js +0 -774
- package/dist/drag-and-drop.js +0 -386
- package/dist/editable-rect.js +0 -450
- package/dist/filter-builder.js +0 -468
- package/dist/float.js +0 -170
- package/dist/form.js +0 -466
- package/dist/gamepad.js +0 -115
- package/dist/icon-data.js +0 -308
- package/dist/icon-types.js +0 -1
- package/dist/icons.js +0 -374
- package/dist/index-iife.js +0 -4
- package/dist/live-example.js +0 -611
- package/dist/localize.js +0 -381
- package/dist/make-sorter.js +0 -119
- package/dist/make-sorter.test.d.ts +0 -1
- package/dist/make-sorter.test.js +0 -48
- package/dist/mapbox.js +0 -161
- package/dist/markdown-viewer.js +0 -173
- package/dist/match-shortcut.js +0 -13
- package/dist/match-shortcut.test.d.ts +0 -1
- package/dist/match-shortcut.test.js +0 -194
- package/dist/menu.js +0 -614
- package/dist/notifications.js +0 -308
- package/dist/password-strength.js +0 -302
- package/dist/playwright.config.d.ts +0 -9
- package/dist/playwright.config.js +0 -73
- package/dist/pop-float.js +0 -231
- package/dist/rating.js +0 -192
- package/dist/rich-text.js +0 -296
- package/dist/segmented.js +0 -298
- package/dist/select.js +0 -427
- package/dist/side-nav.js +0 -106
- package/dist/size-break.js +0 -118
- package/dist/sizer.js +0 -92
- package/dist/src/ab-test.d.ts +0 -14
- package/dist/src/babylon-3d.d.ts +0 -53
- package/dist/src/bodymovin-player.d.ts +0 -32
- package/dist/src/bp-loader.d.ts +0 -0
- package/dist/src/carousel.d.ts +0 -113
- package/dist/src/code-editor.d.ts +0 -27
- package/dist/src/color-input.d.ts +0 -41
- package/dist/src/data-table.d.ts +0 -79
- package/dist/src/drag-and-drop.d.ts +0 -2
- package/dist/src/editable-rect.d.ts +0 -97
- package/dist/src/filter-builder.d.ts +0 -64
- package/dist/src/float.d.ts +0 -18
- package/dist/src/form.d.ts +0 -68
- package/dist/src/gamepad.d.ts +0 -34
- package/dist/src/icon-data.d.ts +0 -309
- package/dist/src/icon-types.d.ts +0 -7
- package/dist/src/icons.d.ts +0 -17
- package/dist/src/index.d.ts +0 -37
- package/dist/src/live-example.d.ts +0 -51
- package/dist/src/localize.d.ts +0 -30
- package/dist/src/make-sorter.d.ts +0 -3
- package/dist/src/mapbox.d.ts +0 -24
- package/dist/src/markdown-viewer.d.ts +0 -15
- package/dist/src/match-shortcut.d.ts +0 -9
- package/dist/src/menu.d.ts +0 -60
- package/dist/src/notifications.d.ts +0 -106
- package/dist/src/password-strength.d.ts +0 -35
- package/dist/src/pop-float.d.ts +0 -10
- package/dist/src/rating.d.ts +0 -62
- package/dist/src/rich-text.d.ts +0 -28
- package/dist/src/segmented.d.ts +0 -80
- package/dist/src/select.d.ts +0 -43
- package/dist/src/side-nav.d.ts +0 -36
- package/dist/src/size-break.d.ts +0 -18
- package/dist/src/sizer.d.ts +0 -34
- package/dist/src/tab-selector.d.ts +0 -91
- package/dist/src/tag-list.d.ts +0 -37
- package/dist/src/track-drag.d.ts +0 -5
- package/dist/src/version.d.ts +0 -1
- package/dist/src/via-tag.d.ts +0 -2
- package/dist/tab-selector.js +0 -326
- package/dist/tag-list.js +0 -375
- package/dist/track-drag.js +0 -143
- package/dist/version.js +0 -1
- package/dist/via-tag.js +0 -102
package/dist/data-table.js
DELETED
|
@@ -1,774 +0,0 @@
|
|
|
1
|
-
/*#
|
|
2
|
-
# table
|
|
3
|
-
|
|
4
|
-
A virtual data-table, configurable via a `columns` array (which will automatically be generated if not provided),
|
|
5
|
-
that displays gigantic tables with fixed headers (and live column-resizing) using a minimum of resources and cpu.
|
|
6
|
-
|
|
7
|
-
```js
|
|
8
|
-
const { dataTable } = xinjsui
|
|
9
|
-
const { input } = xinjs.elements
|
|
10
|
-
|
|
11
|
-
const emojiRequest = await fetch('https://raw.githubusercontent.com/tonioloewald/emoji-metadata/master/emoji-metadata.json')
|
|
12
|
-
const emojiData = await emojiRequest.json()
|
|
13
|
-
|
|
14
|
-
const columns = [
|
|
15
|
-
{
|
|
16
|
-
name: "emoji",
|
|
17
|
-
prop: "chars",
|
|
18
|
-
align: "center",
|
|
19
|
-
width: 80,
|
|
20
|
-
sort: false,
|
|
21
|
-
visible: true
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
prop: "name",
|
|
25
|
-
width: 300,
|
|
26
|
-
// custom cell using xinjs bindings to make the field editable
|
|
27
|
-
dataCell() {
|
|
28
|
-
return input({
|
|
29
|
-
class: 'td',
|
|
30
|
-
bindValue: '^.name',
|
|
31
|
-
title: 'name',
|
|
32
|
-
onMouseup: (event) => { event.stopPropagation() },
|
|
33
|
-
onTouchend: (event) => { event.stopPropagation() },
|
|
34
|
-
})
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
prop: "category",
|
|
39
|
-
sort: "ascending",
|
|
40
|
-
width: 150
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
prop: "subcategory",
|
|
44
|
-
width: 150
|
|
45
|
-
},
|
|
46
|
-
]
|
|
47
|
-
|
|
48
|
-
preview.append(dataTable({
|
|
49
|
-
multiple: true,
|
|
50
|
-
array: emojiData,
|
|
51
|
-
localized: true,
|
|
52
|
-
columns,
|
|
53
|
-
rowHeight: 40,
|
|
54
|
-
pinnedBottom: 2
|
|
55
|
-
}))
|
|
56
|
-
```
|
|
57
|
-
```css
|
|
58
|
-
.preview input.td {
|
|
59
|
-
margin: 0;
|
|
60
|
-
border-radius: 0;
|
|
61
|
-
box-shadow: none !important;
|
|
62
|
-
background: #fff4;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
.preview xin-table {
|
|
66
|
-
height: 100%;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.preview xin-table [part="pinnedTopRows"],
|
|
70
|
-
.preview xin-table [part="pinnedBottomRows"] {
|
|
71
|
-
background: #ddd;
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
> In the preceding example, the `name` column is *editable* (and *bound*, try editing something and scrolling
|
|
76
|
-
> it out of view and back) and `multiple` select is enabled. In the console, you can try `$('xin-table').visibleRows`
|
|
77
|
-
> and $('xin-table').selectedRows`.
|
|
78
|
-
|
|
79
|
-
You can set the `<xin-table>`'s `array`, `columns`, and `filter` properties directly, or set its `value` to:
|
|
80
|
-
|
|
81
|
-
```
|
|
82
|
-
{
|
|
83
|
-
array: any[],
|
|
84
|
-
columns: ColumnOptions[] | null,
|
|
85
|
-
filter?: ArrayFilter
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## `ColumnOptions`
|
|
90
|
-
|
|
91
|
-
You can configure the table's columns by providing it an array of `ColumnOptions`:
|
|
92
|
-
|
|
93
|
-
```
|
|
94
|
-
export interface ColumnOptions {
|
|
95
|
-
name?: string
|
|
96
|
-
prop: string
|
|
97
|
-
width: number
|
|
98
|
-
visible?: boolean
|
|
99
|
-
align?: string
|
|
100
|
-
sort?: false | 'ascending' | 'descending'
|
|
101
|
-
headerCell?: (options: ColumnOptions) => HTMLElement
|
|
102
|
-
dataCell?: (options: ColumnOptions) => HTMLElement
|
|
103
|
-
}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
## Selection
|
|
107
|
-
|
|
108
|
-
`<xin-table>` supports `select` and `multiple` boolean properties allowing rows to be selectable. Selected rows will
|
|
109
|
-
be given the `[aria-selected]` attribute, so style them as you wish.
|
|
110
|
-
|
|
111
|
-
`multiple` select supports shift-clicking and command/meta-clicking.
|
|
112
|
-
|
|
113
|
-
`<xin-table>` provides an `selectionChanged(visibleSelectedRows: any[]): void` callback property allowing you to respond to changes
|
|
114
|
-
in the selection, and also `selectedRows` and `visibleSelectedRows` properties.
|
|
115
|
-
|
|
116
|
-
The following methods are also provided:
|
|
117
|
-
|
|
118
|
-
- `<xin-table>.selectRow(row: any, select = true)` (de)selects specified row
|
|
119
|
-
- `<xin-table>.selectRows(rows?: any[], select = true)` (de)selects specified rows
|
|
120
|
-
- `<xin-table>.deSelect(rows?: any[])` deselects all or specified rows.
|
|
121
|
-
|
|
122
|
-
These are rather fine-grained but they're used internally by the selection code so they may as well be documented.
|
|
123
|
-
|
|
124
|
-
## Sorting
|
|
125
|
-
|
|
126
|
-
By default, the user can sort the table by any column which doesn't have a `sort === false`.
|
|
127
|
-
|
|
128
|
-
You can set the initial sort by setting the `sort` value of a specific column to `ascending`
|
|
129
|
-
or `descending`.
|
|
130
|
-
|
|
131
|
-
You can override this by setting the table's sort function (it's an `Array.sort()` callback)
|
|
132
|
-
to whatever you like, and you can replace the `headerCell` or set the `sort` of each column
|
|
133
|
-
to `false` if you have some specific sorting in mind.
|
|
134
|
-
|
|
135
|
-
You can disable sorting controls by adding the `nosort` attribute to the `<xin-table>`.
|
|
136
|
-
|
|
137
|
-
## Hiding (and Showing) Columns
|
|
138
|
-
|
|
139
|
-
By default, the user can show / hide columns by clicking via the column header menu.
|
|
140
|
-
You can remove this option by adding the `nohide` attribute to the `<xin-table>`
|
|
141
|
-
|
|
142
|
-
## Reordering Columns
|
|
143
|
-
|
|
144
|
-
By default, the user can reorder columns by dragging them around. You can disable this
|
|
145
|
-
by adding the `noreorder` attribute to the `<xin-table>`.
|
|
146
|
-
|
|
147
|
-
## Row Height
|
|
148
|
-
|
|
149
|
-
If you set the `<xin-table>`'s `rowHeight` to `0` it will render all its elements (i.e. not be virtual). This is
|
|
150
|
-
useful for smaller tables, or tables with variable row-heights.
|
|
151
|
-
|
|
152
|
-
## Styling
|
|
153
|
-
|
|
154
|
-
Aside from row height (see previous) the component doesn't use the shadowDOM, so it's easy to override
|
|
155
|
-
its styles.
|
|
156
|
-
|
|
157
|
-
## Pinned Rows
|
|
158
|
-
|
|
159
|
-
The table supports two attributes, `pinnedTop` and `pinnedBottom` that let you pin the specified number
|
|
160
|
-
of top and bottom rows.
|
|
161
|
-
|
|
162
|
-
## Localization
|
|
163
|
-
|
|
164
|
-
`<xin-table>` supports the `localized` attribute which simply causes its default `headerCell`
|
|
165
|
-
to render a `<xin-localized>` element instead of a span for its caption, and localize its
|
|
166
|
-
popup menu.
|
|
167
|
-
|
|
168
|
-
You'll need to make sure your localized strings include:
|
|
169
|
-
|
|
170
|
-
- Sort
|
|
171
|
-
- Show
|
|
172
|
-
- Hide
|
|
173
|
-
- Column
|
|
174
|
-
- Ascending
|
|
175
|
-
- Descending
|
|
176
|
-
|
|
177
|
-
As well as any column names you want localized.
|
|
178
|
-
*/
|
|
179
|
-
import { Component as WebComponent, elements, vars, varDefault, xinValue, getListItem, boxedProxy, } from 'xinjs';
|
|
180
|
-
import { trackDrag } from './track-drag';
|
|
181
|
-
import { icons } from './icons';
|
|
182
|
-
import { popMenu } from './menu';
|
|
183
|
-
import * as dragAndDrop from './drag-and-drop';
|
|
184
|
-
import { xinLocalized, localize } from './localize';
|
|
185
|
-
function defaultWidth(array, prop, charWidth) {
|
|
186
|
-
const example = array.find((item) => item[prop] !== undefined && item[prop] !== null);
|
|
187
|
-
if (example !== undefined) {
|
|
188
|
-
const value = example[prop];
|
|
189
|
-
switch (typeof value) {
|
|
190
|
-
case 'string':
|
|
191
|
-
if (value.match(/^\d+(\.\d+)?$/)) {
|
|
192
|
-
return 6 * charWidth;
|
|
193
|
-
}
|
|
194
|
-
else if (value.includes(' ')) {
|
|
195
|
-
return 20 * charWidth;
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
return 12 * charWidth;
|
|
199
|
-
}
|
|
200
|
-
case 'number':
|
|
201
|
-
return 6 * charWidth;
|
|
202
|
-
case 'boolean':
|
|
203
|
-
return 5 * charWidth;
|
|
204
|
-
case 'object':
|
|
205
|
-
return false;
|
|
206
|
-
default:
|
|
207
|
-
return 8 * charWidth;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return false;
|
|
211
|
-
}
|
|
212
|
-
const { div, span, button, template } = elements;
|
|
213
|
-
const passThru = (array) => array;
|
|
214
|
-
export class DataTable extends WebComponent {
|
|
215
|
-
select = false;
|
|
216
|
-
multiple = false;
|
|
217
|
-
nosort = false;
|
|
218
|
-
nohide = false;
|
|
219
|
-
noreorder = false;
|
|
220
|
-
selectionChanged = () => {
|
|
221
|
-
/* do not care */
|
|
222
|
-
};
|
|
223
|
-
localized = false;
|
|
224
|
-
selectedKey = Symbol('selected');
|
|
225
|
-
selectBinding = (elt, obj) => {
|
|
226
|
-
elt.toggleAttribute('aria-selected', obj[this.selectedKey] === true);
|
|
227
|
-
};
|
|
228
|
-
pinnedTop = 0;
|
|
229
|
-
pinnedBottom = 0;
|
|
230
|
-
maxVisibleRows = 10000;
|
|
231
|
-
get value() {
|
|
232
|
-
return {
|
|
233
|
-
array: this.array,
|
|
234
|
-
filter: this.filter,
|
|
235
|
-
columns: this.columns,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
set value(data) {
|
|
239
|
-
const { array, columns, filter } = xinValue(data);
|
|
240
|
-
if (this._array !== array ||
|
|
241
|
-
this._columns !== columns ||
|
|
242
|
-
this._filter !== filter) {
|
|
243
|
-
this.queueRender();
|
|
244
|
-
}
|
|
245
|
-
this._array = array || [];
|
|
246
|
-
this._columns = columns || null;
|
|
247
|
-
this._filter = filter || passThru;
|
|
248
|
-
}
|
|
249
|
-
rowData = {
|
|
250
|
-
visible: [],
|
|
251
|
-
pinnedTop: [],
|
|
252
|
-
pinnedBottom: [],
|
|
253
|
-
};
|
|
254
|
-
_array = [];
|
|
255
|
-
_columns = null;
|
|
256
|
-
_filter = passThru;
|
|
257
|
-
charWidth = 15;
|
|
258
|
-
rowHeight = 30;
|
|
259
|
-
minColumnWidth = 30;
|
|
260
|
-
get virtual() {
|
|
261
|
-
return this.rowHeight > 0 ? { height: this.rowHeight } : undefined;
|
|
262
|
-
}
|
|
263
|
-
constructor() {
|
|
264
|
-
super();
|
|
265
|
-
this.rowData = boxedProxy({
|
|
266
|
-
[this.instanceId]: this.rowData,
|
|
267
|
-
})[this.instanceId];
|
|
268
|
-
this.initAttributes('rowHeight', 'charWidth', 'minColumnWidth', 'select', 'multiple', 'pinnedTop', 'pinnedBottom', 'nosort', 'nohide', 'noreorder', 'localized');
|
|
269
|
-
}
|
|
270
|
-
get array() {
|
|
271
|
-
return this._array;
|
|
272
|
-
}
|
|
273
|
-
set array(newArray) {
|
|
274
|
-
this._array = xinValue(newArray);
|
|
275
|
-
this.queueRender();
|
|
276
|
-
}
|
|
277
|
-
get filter() {
|
|
278
|
-
return this._filter;
|
|
279
|
-
}
|
|
280
|
-
set filter(filterFunc) {
|
|
281
|
-
if (this._filter !== filterFunc) {
|
|
282
|
-
this._filter = filterFunc;
|
|
283
|
-
this.queueRender();
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
get sort() {
|
|
287
|
-
if (this._sort) {
|
|
288
|
-
return this._sort;
|
|
289
|
-
}
|
|
290
|
-
const sortColumn = this._columns?.find((c) => c.sort === 'ascending' || c.sort === 'descending');
|
|
291
|
-
if (!sortColumn) {
|
|
292
|
-
return undefined;
|
|
293
|
-
}
|
|
294
|
-
const { prop } = sortColumn;
|
|
295
|
-
return sortColumn.sort === 'ascending'
|
|
296
|
-
? (a, b) => (a[prop] > b[prop] ? 1 : -1)
|
|
297
|
-
: (a, b) => (a[prop] > b[prop] ? -1 : 1);
|
|
298
|
-
}
|
|
299
|
-
set sort(sortFunc) {
|
|
300
|
-
if (this._sort !== sortFunc) {
|
|
301
|
-
this._sort = sortFunc;
|
|
302
|
-
this.queueRender();
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
get columns() {
|
|
306
|
-
if (!Array.isArray(this._columns)) {
|
|
307
|
-
const { _array } = this;
|
|
308
|
-
this._columns = Object.keys(_array[0] || {}).map((prop) => {
|
|
309
|
-
const width = defaultWidth(_array, prop, this.charWidth);
|
|
310
|
-
return {
|
|
311
|
-
name: prop.replace(/([a-z])([A-Z])/g, '$1 $2').toLocaleLowerCase(),
|
|
312
|
-
prop,
|
|
313
|
-
align: typeof _array[0][prop] === 'number' ||
|
|
314
|
-
(_array[0][prop] !== '' && !isNaN(_array[0][prop]))
|
|
315
|
-
? 'right'
|
|
316
|
-
: 'left',
|
|
317
|
-
visible: width !== false,
|
|
318
|
-
width: width ? width : 0,
|
|
319
|
-
};
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
return this._columns;
|
|
323
|
-
}
|
|
324
|
-
set columns(newColumns) {
|
|
325
|
-
this._columns = newColumns;
|
|
326
|
-
this.queueRender();
|
|
327
|
-
}
|
|
328
|
-
get visibleColumns() {
|
|
329
|
-
return this.columns.filter((c) => c.visible !== false);
|
|
330
|
-
}
|
|
331
|
-
content = null;
|
|
332
|
-
getColumn(event) {
|
|
333
|
-
const x = (event.touches !== undefined ? event.touches[0].clientX : event.clientX) -
|
|
334
|
-
this.getBoundingClientRect().x;
|
|
335
|
-
const epsilon = event.touches !== undefined ? 20 : 5;
|
|
336
|
-
let boundaryX = 0;
|
|
337
|
-
const log = [];
|
|
338
|
-
const column = this.visibleColumns.find((options) => {
|
|
339
|
-
if (options.visible !== false) {
|
|
340
|
-
boundaryX += options.width;
|
|
341
|
-
log.push(boundaryX);
|
|
342
|
-
return Math.abs(x - boundaryX) < epsilon;
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
return column;
|
|
346
|
-
}
|
|
347
|
-
setCursor = (event) => {
|
|
348
|
-
const column = this.getColumn(event);
|
|
349
|
-
if (column !== undefined) {
|
|
350
|
-
this.style.cursor = 'col-resize';
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
this.style.cursor = '';
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
resizeColumn = (event) => {
|
|
357
|
-
const column = this.getColumn(event);
|
|
358
|
-
if (column !== undefined) {
|
|
359
|
-
const origWidth = Number(column.width);
|
|
360
|
-
const isTouchEvent = event.touches !== undefined;
|
|
361
|
-
const touchIdentifier = isTouchEvent
|
|
362
|
-
? event.touches[0].identifier
|
|
363
|
-
: undefined;
|
|
364
|
-
trackDrag(event, (dx, _dy, event) => {
|
|
365
|
-
const touch = isTouchEvent
|
|
366
|
-
? [...event.touches].find((touch) => touch.identifier === touchIdentifier)
|
|
367
|
-
: true;
|
|
368
|
-
if (touch === undefined) {
|
|
369
|
-
return true;
|
|
370
|
-
}
|
|
371
|
-
const width = origWidth + dx;
|
|
372
|
-
column.width =
|
|
373
|
-
width > this.minColumnWidth ? width : this.minColumnWidth;
|
|
374
|
-
this.setColumnWidths();
|
|
375
|
-
if (event.type === 'mouseup') {
|
|
376
|
-
return true;
|
|
377
|
-
}
|
|
378
|
-
}, 'col-resize');
|
|
379
|
-
}
|
|
380
|
-
};
|
|
381
|
-
selectRow(row, select = true) {
|
|
382
|
-
if (select) {
|
|
383
|
-
row[this.selectedKey] = true;
|
|
384
|
-
}
|
|
385
|
-
else {
|
|
386
|
-
delete row[this.selectedKey];
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
selectRows(rows, select = true) {
|
|
390
|
-
for (const row of rows || this.array) {
|
|
391
|
-
this.selectRow(row, select);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
deSelect(rows) {
|
|
395
|
-
this.selectRows(rows, false);
|
|
396
|
-
}
|
|
397
|
-
// tracking click / shift-click
|
|
398
|
-
rangeStart;
|
|
399
|
-
updateSelection = (event) => {
|
|
400
|
-
if (!this.select && !this.multiple) {
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
const { target } = event;
|
|
404
|
-
if (!(target instanceof HTMLElement)) {
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
const tr = target.closest('.tr');
|
|
408
|
-
if (!(tr instanceof HTMLElement)) {
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
const pickedItem = getListItem(tr);
|
|
412
|
-
if (pickedItem === false) {
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
const mouseEvent = event;
|
|
416
|
-
// prevent ugly selection artifacts
|
|
417
|
-
const selection = window.getSelection();
|
|
418
|
-
if (selection !== null) {
|
|
419
|
-
selection.removeAllRanges();
|
|
420
|
-
}
|
|
421
|
-
const rows = this.visibleRows;
|
|
422
|
-
if (this.multiple &&
|
|
423
|
-
mouseEvent.shiftKey &&
|
|
424
|
-
rows.length > 0 &&
|
|
425
|
-
this.rangeStart !== pickedItem) {
|
|
426
|
-
const mode = this.rangeStart === undefined ||
|
|
427
|
-
this.rangeStart[this.selectedKey] === true;
|
|
428
|
-
const [start, finish] = [
|
|
429
|
-
this.rangeStart !== undefined ? rows.indexOf(this.rangeStart) : 0,
|
|
430
|
-
rows.indexOf(pickedItem),
|
|
431
|
-
].sort((a, b) => a - b);
|
|
432
|
-
// if start is -1 then one of the items is no longer visible
|
|
433
|
-
if (start > -1) {
|
|
434
|
-
for (let idx = start; idx <= finish; idx++) {
|
|
435
|
-
const row = rows[idx];
|
|
436
|
-
this.selectRow(row, mode);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
else if (this.multiple && mouseEvent.metaKey) {
|
|
441
|
-
this.selectRow(pickedItem, !pickedItem[this.selectedKey]);
|
|
442
|
-
const pickedIndex = rows.indexOf(pickedItem);
|
|
443
|
-
const nextItem = rows[pickedIndex + 1];
|
|
444
|
-
const previousItem = pickedIndex > 0 ? rows[pickedIndex - 1] : undefined;
|
|
445
|
-
if (nextItem !== undefined && nextItem[this.selectedKey] === true) {
|
|
446
|
-
this.rangeStart = nextItem;
|
|
447
|
-
}
|
|
448
|
-
else if (previousItem !== undefined &&
|
|
449
|
-
previousItem[this.selectedKey] === true) {
|
|
450
|
-
this.rangeStart = previousItem;
|
|
451
|
-
}
|
|
452
|
-
else {
|
|
453
|
-
this.rangeStart = undefined;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
this.rangeStart = pickedItem;
|
|
458
|
-
this.deSelect();
|
|
459
|
-
this.selectRow(pickedItem, true);
|
|
460
|
-
}
|
|
461
|
-
this.selectionChanged(this.visibleSelectedRows);
|
|
462
|
-
for (const row of Array.from(this.querySelectorAll('.tr'))) {
|
|
463
|
-
const item = getListItem(row);
|
|
464
|
-
this.selectBinding(row, item);
|
|
465
|
-
}
|
|
466
|
-
};
|
|
467
|
-
connectedCallback() {
|
|
468
|
-
super.connectedCallback();
|
|
469
|
-
this.addEventListener('mousemove', this.setCursor);
|
|
470
|
-
this.addEventListener('mousedown', this.resizeColumn);
|
|
471
|
-
this.addEventListener('touchstart', this.resizeColumn, { passive: true });
|
|
472
|
-
this.addEventListener('mouseup', this.updateSelection);
|
|
473
|
-
this.addEventListener('touchend', this.updateSelection);
|
|
474
|
-
}
|
|
475
|
-
setColumnWidths() {
|
|
476
|
-
this.style.setProperty('--grid-columns', this.visibleColumns.map((c) => c.width + 'px').join(' '));
|
|
477
|
-
this.style.setProperty('--grid-row-width', this.visibleColumns.reduce((w, c) => w + c.width, 0) + 'px');
|
|
478
|
-
}
|
|
479
|
-
sortByColumn = (columnOptions, direction = 'auto') => {
|
|
480
|
-
for (const column of this.columns.filter((c) => xinValue(c.sort) !== false)) {
|
|
481
|
-
if (xinValue(column) === columnOptions) {
|
|
482
|
-
if (direction === 'auto') {
|
|
483
|
-
column.sort = column.sort === 'ascending' ? 'descending' : 'ascending';
|
|
484
|
-
}
|
|
485
|
-
else {
|
|
486
|
-
column.sort = direction;
|
|
487
|
-
}
|
|
488
|
-
this.queueRender();
|
|
489
|
-
}
|
|
490
|
-
else {
|
|
491
|
-
delete column.sort;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
};
|
|
495
|
-
popColumnMenu = (target, options) => {
|
|
496
|
-
const { sortByColumn } = this;
|
|
497
|
-
const hiddenColumns = this.columns.filter((column) => column.visible === false);
|
|
498
|
-
const queueRender = this.queueRender.bind(this);
|
|
499
|
-
const menu = [];
|
|
500
|
-
if (!this.nosort && options.sort !== false) {
|
|
501
|
-
menu.push({
|
|
502
|
-
caption: this.localized
|
|
503
|
-
? `${localize('Sort')} ${localize('Ascending')}`
|
|
504
|
-
: 'Sort Ascending',
|
|
505
|
-
icon: 'sortAscending',
|
|
506
|
-
action() {
|
|
507
|
-
sortByColumn(options);
|
|
508
|
-
},
|
|
509
|
-
}, {
|
|
510
|
-
caption: this.localized
|
|
511
|
-
? `${localize('Sort')} ${localize('Descending')}`
|
|
512
|
-
: 'Sort Ascending',
|
|
513
|
-
icon: 'sortDescending',
|
|
514
|
-
action() {
|
|
515
|
-
sortByColumn(options, 'descending');
|
|
516
|
-
},
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
|
-
if (!this.nohide) {
|
|
520
|
-
if (menu.length) {
|
|
521
|
-
menu.push(null);
|
|
522
|
-
}
|
|
523
|
-
menu.push({
|
|
524
|
-
caption: this.localized
|
|
525
|
-
? `${localize('Hide')} ${localize('Column')}`
|
|
526
|
-
: 'Hide Column',
|
|
527
|
-
icon: 'eyeOff',
|
|
528
|
-
enabled: () => options.visible !== true,
|
|
529
|
-
action() {
|
|
530
|
-
options.visible = false;
|
|
531
|
-
queueRender();
|
|
532
|
-
},
|
|
533
|
-
}, {
|
|
534
|
-
caption: this.localized
|
|
535
|
-
? `${localize('Show')} ${localize('Column')}`
|
|
536
|
-
: 'Show Column',
|
|
537
|
-
icon: 'eye',
|
|
538
|
-
enabled: () => hiddenColumns.length > 0,
|
|
539
|
-
menuItems: hiddenColumns.map((column) => {
|
|
540
|
-
return {
|
|
541
|
-
caption: column.name || column.prop,
|
|
542
|
-
action() {
|
|
543
|
-
delete column.visible;
|
|
544
|
-
queueRender();
|
|
545
|
-
},
|
|
546
|
-
};
|
|
547
|
-
}),
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
popMenu({
|
|
551
|
-
target,
|
|
552
|
-
localized: this.localized,
|
|
553
|
-
menuItems: menu,
|
|
554
|
-
});
|
|
555
|
-
};
|
|
556
|
-
get captionSpan() {
|
|
557
|
-
return this.localized ? xinLocalized : span;
|
|
558
|
-
}
|
|
559
|
-
headerCell = (options) => {
|
|
560
|
-
const { popColumnMenu } = this;
|
|
561
|
-
let ariaSort = 'none';
|
|
562
|
-
let sortIcon;
|
|
563
|
-
switch (options.sort) {
|
|
564
|
-
case 'ascending':
|
|
565
|
-
sortIcon = icons.sortAscending();
|
|
566
|
-
ariaSort = 'descending';
|
|
567
|
-
break;
|
|
568
|
-
case false:
|
|
569
|
-
break;
|
|
570
|
-
default:
|
|
571
|
-
break;
|
|
572
|
-
case 'descending':
|
|
573
|
-
ariaSort = 'ascending';
|
|
574
|
-
sortIcon = icons.sortDescending();
|
|
575
|
-
}
|
|
576
|
-
const menuButton = !(this.nosort && this.nohide)
|
|
577
|
-
? button({
|
|
578
|
-
class: 'menu-trigger',
|
|
579
|
-
onClick(event) {
|
|
580
|
-
popColumnMenu(event.target, options);
|
|
581
|
-
event.stopPropagation();
|
|
582
|
-
},
|
|
583
|
-
}, sortIcon || icons.moreVertical())
|
|
584
|
-
: {};
|
|
585
|
-
return options.headerCell !== undefined
|
|
586
|
-
? options.headerCell(options)
|
|
587
|
-
: span({
|
|
588
|
-
class: 'th',
|
|
589
|
-
role: 'columnheader',
|
|
590
|
-
ariaSort,
|
|
591
|
-
style: {
|
|
592
|
-
...this.cellStyle,
|
|
593
|
-
textAlign: options.align || 'left',
|
|
594
|
-
},
|
|
595
|
-
}, this.captionSpan(typeof options.name === 'string' ? options.name : options.prop), span({ style: { flex: '1' } }), menuButton);
|
|
596
|
-
};
|
|
597
|
-
dataCell = (options) => {
|
|
598
|
-
if (options.dataCell !== undefined) {
|
|
599
|
-
return options.dataCell(options);
|
|
600
|
-
}
|
|
601
|
-
return span({
|
|
602
|
-
class: 'td',
|
|
603
|
-
role: 'cell',
|
|
604
|
-
style: {
|
|
605
|
-
...this.cellStyle,
|
|
606
|
-
textAlign: options.align || 'left',
|
|
607
|
-
},
|
|
608
|
-
bindText: `^.${options.prop}`,
|
|
609
|
-
});
|
|
610
|
-
};
|
|
611
|
-
get visibleRows() {
|
|
612
|
-
return xinValue(this.rowData.visible);
|
|
613
|
-
}
|
|
614
|
-
get visibleSelectedRows() {
|
|
615
|
-
return this.visibleRows.filter((obj) => obj[this.selectedKey]);
|
|
616
|
-
}
|
|
617
|
-
get selectedRows() {
|
|
618
|
-
return this.array.filter((obj) => obj[this.selectedKey]);
|
|
619
|
-
}
|
|
620
|
-
rowTemplate(columns) {
|
|
621
|
-
return template(div({
|
|
622
|
-
class: 'tr',
|
|
623
|
-
role: 'row',
|
|
624
|
-
bind: {
|
|
625
|
-
value: '^',
|
|
626
|
-
binding: { toDOM: this.selectBinding },
|
|
627
|
-
},
|
|
628
|
-
}, ...columns.map(this.dataCell)));
|
|
629
|
-
}
|
|
630
|
-
draggedColumn;
|
|
631
|
-
dropColumn = (event) => {
|
|
632
|
-
const target = event.target.closest('.drag-over');
|
|
633
|
-
const targetIndex = Array.from(target.parentElement.children).indexOf(target);
|
|
634
|
-
const dropped = this.visibleColumns[targetIndex];
|
|
635
|
-
const draggedIndex = this.columns.indexOf(this.draggedColumn);
|
|
636
|
-
const droppedIndex = this.columns.indexOf(dropped);
|
|
637
|
-
this.columns.splice(draggedIndex, 1);
|
|
638
|
-
this.columns.splice(droppedIndex, 0, this.draggedColumn);
|
|
639
|
-
console.log({ event, target, targetIndex, draggedIndex, droppedIndex });
|
|
640
|
-
this.queueRender();
|
|
641
|
-
event.preventDefault();
|
|
642
|
-
event.stopPropagation();
|
|
643
|
-
};
|
|
644
|
-
render() {
|
|
645
|
-
super.render();
|
|
646
|
-
this.rowData.pinnedTop =
|
|
647
|
-
this.pinnedTop > 0 ? this._array.slice(0, this.pinnedTop) : [];
|
|
648
|
-
this.rowData.pinnedBottom =
|
|
649
|
-
this.pinnedBottom > 0
|
|
650
|
-
? this._array.slice(this._array.length - this.pinnedBottom)
|
|
651
|
-
: [];
|
|
652
|
-
this.rowData.visible = this.filter(this._array.slice(this.pinnedTop, Math.min(this.maxVisibleRows, this._array.length - this.pinnedTop - this.pinnedBottom)));
|
|
653
|
-
const { sort } = this;
|
|
654
|
-
if (sort) {
|
|
655
|
-
this.rowData.visible.sort(sort);
|
|
656
|
-
}
|
|
657
|
-
this.textContent = '';
|
|
658
|
-
this.style.display = 'flex';
|
|
659
|
-
this.style.flexDirection = 'column';
|
|
660
|
-
const { visibleColumns } = this;
|
|
661
|
-
this.style.setProperty('--row-height', `${this.rowHeight}px`);
|
|
662
|
-
this.setColumnWidths();
|
|
663
|
-
if (!this.noreorder) {
|
|
664
|
-
dragAndDrop.init();
|
|
665
|
-
}
|
|
666
|
-
const dragId = this.instanceId + '-column-header';
|
|
667
|
-
const columnHeaders = visibleColumns.map((column) => {
|
|
668
|
-
const header = this.headerCell(column);
|
|
669
|
-
if (!this.noreorder) {
|
|
670
|
-
header.setAttribute('draggable', 'true');
|
|
671
|
-
header.dataset.drag = dragId;
|
|
672
|
-
header.dataset.drop = dragId;
|
|
673
|
-
header.addEventListener('dragstart', () => {
|
|
674
|
-
this.draggedColumn = column;
|
|
675
|
-
});
|
|
676
|
-
header.addEventListener('drop', this.dropColumn);
|
|
677
|
-
}
|
|
678
|
-
return header;
|
|
679
|
-
});
|
|
680
|
-
this.append(div({ class: 'thead', role: 'rowgroup', style: { touchAction: 'none' } }, div({
|
|
681
|
-
class: 'tr',
|
|
682
|
-
role: 'row',
|
|
683
|
-
}, ...columnHeaders)));
|
|
684
|
-
if (this.pinnedTop > 0) {
|
|
685
|
-
this.append(div({
|
|
686
|
-
part: 'pinnedTopRows',
|
|
687
|
-
class: 'tbody',
|
|
688
|
-
role: 'rowgroup',
|
|
689
|
-
style: {
|
|
690
|
-
flex: '0 0 auto',
|
|
691
|
-
overflow: 'hidden',
|
|
692
|
-
height: `${this.rowHeight * this.pinnedTop}px`,
|
|
693
|
-
},
|
|
694
|
-
bindList: {
|
|
695
|
-
value: this.rowData.pinnedTop,
|
|
696
|
-
virtual: this.virtual,
|
|
697
|
-
},
|
|
698
|
-
}, this.rowTemplate(visibleColumns)));
|
|
699
|
-
}
|
|
700
|
-
this.append(div({
|
|
701
|
-
part: 'visibleRows',
|
|
702
|
-
class: 'tbody',
|
|
703
|
-
role: 'rowgroup',
|
|
704
|
-
style: {
|
|
705
|
-
content: ' ',
|
|
706
|
-
minHeight: '100px',
|
|
707
|
-
flex: '1 1 100px',
|
|
708
|
-
overflow: 'hidden auto',
|
|
709
|
-
},
|
|
710
|
-
bindList: {
|
|
711
|
-
value: this.rowData.visible,
|
|
712
|
-
virtual: this.virtual,
|
|
713
|
-
},
|
|
714
|
-
}, this.rowTemplate(visibleColumns)));
|
|
715
|
-
if (this.pinnedBottom > 0) {
|
|
716
|
-
this.append(div({
|
|
717
|
-
part: 'pinnedBottomRows',
|
|
718
|
-
class: 'tbody',
|
|
719
|
-
role: 'rowgroup',
|
|
720
|
-
style: {
|
|
721
|
-
flex: '0 0 auto',
|
|
722
|
-
overflow: 'hidden',
|
|
723
|
-
height: `${this.rowHeight * this.pinnedBottom}px`,
|
|
724
|
-
},
|
|
725
|
-
bindList: {
|
|
726
|
-
value: this.rowData.pinnedBottom,
|
|
727
|
-
virtual: this.virtual,
|
|
728
|
-
},
|
|
729
|
-
}, this.rowTemplate(visibleColumns)));
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
export const dataTable = DataTable.elementCreator({
|
|
734
|
-
tag: 'xin-table',
|
|
735
|
-
styleSpec: {
|
|
736
|
-
':host': {
|
|
737
|
-
overflow: 'auto hidden',
|
|
738
|
-
},
|
|
739
|
-
':host .thead, :host .tbody': {
|
|
740
|
-
width: vars.gridRowWidth,
|
|
741
|
-
},
|
|
742
|
-
':host .tr': {
|
|
743
|
-
display: 'grid',
|
|
744
|
-
gridTemplateColumns: vars.gridColumns,
|
|
745
|
-
height: vars.rowHeight,
|
|
746
|
-
lineHeight: vars.rowHeight,
|
|
747
|
-
},
|
|
748
|
-
':host .td, :host .th': {
|
|
749
|
-
overflow: 'hidden',
|
|
750
|
-
whiteSpace: 'nowrap',
|
|
751
|
-
textOverflow: 'ellipsis',
|
|
752
|
-
display: 'flex',
|
|
753
|
-
alignItems: 'center',
|
|
754
|
-
},
|
|
755
|
-
':host .th .menu-trigger': {
|
|
756
|
-
color: 'currentColor',
|
|
757
|
-
background: 'none',
|
|
758
|
-
padding: 0,
|
|
759
|
-
lineHeight: vars.touchSize,
|
|
760
|
-
height: vars.touchSize,
|
|
761
|
-
width: vars.touchSize,
|
|
762
|
-
},
|
|
763
|
-
':host [draggable="true"]': {
|
|
764
|
-
cursor: 'ew-resize',
|
|
765
|
-
},
|
|
766
|
-
':host [draggable="true"]:active': {
|
|
767
|
-
background: varDefault.draggedHeaderBg('#0004'),
|
|
768
|
-
color: varDefault.draggedHeaderColor('#fff'),
|
|
769
|
-
},
|
|
770
|
-
':host .drag-over': {
|
|
771
|
-
background: varDefault.dropHeaderBg('#fff4'),
|
|
772
|
-
},
|
|
773
|
-
},
|
|
774
|
-
});
|