@toolbox-web/grid 0.0.4 → 0.0.6
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 +24 -0
- package/all.d.ts +1709 -135
- package/all.js +745 -645
- package/all.js.map +1 -1
- package/index.d.ts +161 -1
- package/index.js +1050 -913
- package/index.js.map +1 -1
- package/lib/plugins/clipboard/index.js +110 -52
- package/lib/plugins/clipboard/index.js.map +1 -1
- package/lib/plugins/column-virtualization/index.js +78 -20
- package/lib/plugins/column-virtualization/index.js.map +1 -1
- package/lib/plugins/context-menu/index.js +163 -95
- package/lib/plugins/context-menu/index.js.map +1 -1
- package/lib/plugins/export/index.js +93 -35
- package/lib/plugins/export/index.js.map +1 -1
- package/lib/plugins/filtering/index.js +188 -133
- package/lib/plugins/filtering/index.js.map +1 -1
- package/lib/plugins/grouping-columns/index.js +69 -11
- package/lib/plugins/grouping-columns/index.js.map +1 -1
- package/lib/plugins/grouping-rows/index.js +111 -55
- package/lib/plugins/grouping-rows/index.js.map +1 -1
- package/lib/plugins/master-detail/index.js +196 -51
- package/lib/plugins/master-detail/index.js.map +1 -1
- package/lib/plugins/multi-sort/index.js +104 -46
- package/lib/plugins/multi-sort/index.js.map +1 -1
- package/lib/plugins/pinned-columns/index.js +74 -16
- package/lib/plugins/pinned-columns/index.js.map +1 -1
- package/lib/plugins/pinned-rows/index.js +65 -7
- package/lib/plugins/pinned-rows/index.js.map +1 -1
- package/lib/plugins/pivot/index.js +117 -59
- package/lib/plugins/pivot/index.js.map +1 -1
- package/lib/plugins/reorder/index.js +103 -45
- package/lib/plugins/reorder/index.js.map +1 -1
- package/lib/plugins/selection/index.js +139 -81
- package/lib/plugins/selection/index.js.map +1 -1
- package/lib/plugins/server-side/index.js +96 -38
- package/lib/plugins/server-side/index.js.map +1 -1
- package/lib/plugins/tree/index.js +108 -47
- package/lib/plugins/tree/index.js.map +1 -1
- package/lib/plugins/undo-redo/index.js +70 -12
- package/lib/plugins/undo-redo/index.js.map +1 -1
- package/lib/plugins/visibility/index.js +82 -24
- package/lib/plugins/visibility/index.js.map +1 -1
- package/package.json +1 -1
- package/umd/grid.all.umd.js +31 -31
- package/umd/grid.all.umd.js.map +1 -1
- package/umd/grid.umd.js +15 -15
- package/umd/grid.umd.js.map +1 -1
- package/umd/plugins/context-menu.umd.js +2 -2
- package/umd/plugins/context-menu.umd.js.map +1 -1
- package/umd/plugins/filtering.umd.js +3 -3
- package/umd/plugins/filtering.umd.js.map +1 -1
- package/umd/plugins/grouping-rows.umd.js +2 -2
- package/umd/plugins/grouping-rows.umd.js.map +1 -1
- package/umd/plugins/master-detail.umd.js +2 -2
- package/umd/plugins/master-detail.umd.js.map +1 -1
- package/umd/plugins/multi-sort.umd.js +1 -1
- package/umd/plugins/multi-sort.umd.js.map +1 -1
- package/umd/plugins/reorder.umd.js +1 -1
- package/umd/plugins/reorder.umd.js.map +1 -1
- package/umd/plugins/tree.umd.js +2 -2
- package/umd/plugins/tree.umd.js.map +1 -1
- package/umd/plugins/visibility.umd.js +1 -1
- package/umd/plugins/visibility.umd.js.map +1 -1
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
const v = {
|
|
2
|
+
expand: "▶",
|
|
3
|
+
collapse: "▼",
|
|
4
|
+
sortAsc: "▲",
|
|
5
|
+
sortDesc: "▼",
|
|
6
|
+
sortNone: "⇅",
|
|
7
|
+
submenuArrow: "▶",
|
|
8
|
+
dragHandle: "⋮⋮"
|
|
9
|
+
};
|
|
10
|
+
class y {
|
|
2
11
|
/** Plugin version - override in subclass if needed */
|
|
3
12
|
version = "1.0.0";
|
|
4
13
|
/** CSS styles to inject into the grid's shadow DOM */
|
|
@@ -49,8 +58,8 @@ class v {
|
|
|
49
58
|
/**
|
|
50
59
|
* Emit a custom event from the grid.
|
|
51
60
|
*/
|
|
52
|
-
emit(e,
|
|
53
|
-
this.grid?.dispatchEvent?.(new CustomEvent(e, { detail:
|
|
61
|
+
emit(e, n) {
|
|
62
|
+
this.grid?.dispatchEvent?.(new CustomEvent(e, { detail: n, bubbles: !0 }));
|
|
54
63
|
}
|
|
55
64
|
/**
|
|
56
65
|
* Request a re-render of the grid.
|
|
@@ -97,6 +106,55 @@ class v {
|
|
|
97
106
|
get shadowRoot() {
|
|
98
107
|
return this.grid?.shadowRoot ?? null;
|
|
99
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Get the disconnect signal for event listener cleanup.
|
|
111
|
+
* This signal is aborted when the grid disconnects from the DOM.
|
|
112
|
+
* Use this when adding event listeners that should be cleaned up automatically.
|
|
113
|
+
*
|
|
114
|
+
* Best for:
|
|
115
|
+
* - Document/window-level listeners added in attach()
|
|
116
|
+
* - Listeners on the grid element itself
|
|
117
|
+
* - Any listener that should persist across renders
|
|
118
|
+
*
|
|
119
|
+
* Not needed for:
|
|
120
|
+
* - Listeners on elements created in afterRender() (removed with element)
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* element.addEventListener('click', handler, { signal: this.disconnectSignal });
|
|
124
|
+
* document.addEventListener('keydown', handler, { signal: this.disconnectSignal });
|
|
125
|
+
*/
|
|
126
|
+
get disconnectSignal() {
|
|
127
|
+
return this.grid?.disconnectSignal;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get the grid-level icons configuration.
|
|
131
|
+
* Returns merged icons (user config + defaults).
|
|
132
|
+
*/
|
|
133
|
+
get gridIcons() {
|
|
134
|
+
const e = this.grid?.gridConfig?.icons ?? {};
|
|
135
|
+
return { ...v, ...e };
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Resolve an icon value to string or HTMLElement.
|
|
139
|
+
* Checks plugin config first, then grid-level icons, then defaults.
|
|
140
|
+
*
|
|
141
|
+
* @param iconKey - The icon key in GridIcons (e.g., 'expand', 'collapse')
|
|
142
|
+
* @param pluginOverride - Optional plugin-level override
|
|
143
|
+
* @returns The resolved icon value
|
|
144
|
+
*/
|
|
145
|
+
resolveIcon(e, n) {
|
|
146
|
+
return n !== void 0 ? n : this.gridIcons[e];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Set an icon value on an element.
|
|
150
|
+
* Handles both string (text/HTML) and HTMLElement values.
|
|
151
|
+
*
|
|
152
|
+
* @param element - The element to set the icon on
|
|
153
|
+
* @param icon - The icon value (string or HTMLElement)
|
|
154
|
+
*/
|
|
155
|
+
setIcon(e, n) {
|
|
156
|
+
typeof n == "string" ? e.innerHTML = n : n instanceof HTMLElement && (e.innerHTML = "", e.appendChild(n.cloneNode(!0)));
|
|
157
|
+
}
|
|
100
158
|
/**
|
|
101
159
|
* Log a warning message.
|
|
102
160
|
*/
|
|
@@ -104,58 +162,58 @@ class v {
|
|
|
104
162
|
console.warn(`[tbw-grid:${this.name}] ${e}`);
|
|
105
163
|
}
|
|
106
164
|
}
|
|
107
|
-
function
|
|
108
|
-
return (typeof
|
|
165
|
+
function b(s, e) {
|
|
166
|
+
return (typeof s == "function" ? s(e) : s).filter((i) => !(i.hidden === !0 || typeof i.hidden == "function" && i.hidden(e)));
|
|
109
167
|
}
|
|
110
|
-
function
|
|
111
|
-
return
|
|
168
|
+
function C(s, e) {
|
|
169
|
+
return s.disabled === !0 ? !0 : typeof s.disabled == "function" ? s.disabled(e) : !1;
|
|
112
170
|
}
|
|
113
|
-
function x(
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
for (const
|
|
117
|
-
if (
|
|
118
|
-
const
|
|
119
|
-
|
|
171
|
+
function x(s, e, n, i = v.submenuArrow) {
|
|
172
|
+
const l = document.createElement("div");
|
|
173
|
+
l.className = "tbw-context-menu", l.setAttribute("role", "menu");
|
|
174
|
+
for (const o of s) {
|
|
175
|
+
if (o.separator) {
|
|
176
|
+
const r = document.createElement("div");
|
|
177
|
+
r.className = "tbw-context-menu-separator", r.setAttribute("role", "separator"), l.appendChild(r);
|
|
120
178
|
continue;
|
|
121
179
|
}
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
if (
|
|
126
|
-
const
|
|
127
|
-
|
|
180
|
+
const t = document.createElement("div");
|
|
181
|
+
t.className = "tbw-context-menu-item", o.cssClass && t.classList.add(o.cssClass), t.setAttribute("role", "menuitem"), t.setAttribute("data-id", o.id);
|
|
182
|
+
const c = C(o, e);
|
|
183
|
+
if (c && (t.classList.add("disabled"), t.setAttribute("aria-disabled", "true")), o.icon) {
|
|
184
|
+
const r = document.createElement("span");
|
|
185
|
+
r.className = "tbw-context-menu-icon", r.innerHTML = o.icon, t.appendChild(r);
|
|
128
186
|
}
|
|
129
|
-
const
|
|
130
|
-
if (
|
|
131
|
-
const
|
|
132
|
-
|
|
187
|
+
const d = document.createElement("span");
|
|
188
|
+
if (d.className = "tbw-context-menu-label", d.textContent = o.name, t.appendChild(d), o.shortcut) {
|
|
189
|
+
const r = document.createElement("span");
|
|
190
|
+
r.className = "tbw-context-menu-shortcut", r.textContent = o.shortcut, t.appendChild(r);
|
|
133
191
|
}
|
|
134
|
-
if (
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
if (
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
}),
|
|
141
|
-
const
|
|
142
|
-
|
|
192
|
+
if (o.subMenu?.length) {
|
|
193
|
+
const r = document.createElement("span");
|
|
194
|
+
r.className = "tbw-context-menu-arrow", typeof i == "string" ? r.innerHTML = i : i instanceof HTMLElement && r.appendChild(i.cloneNode(!0)), t.appendChild(r), t.addEventListener("mouseenter", () => {
|
|
195
|
+
if (t.querySelector(".tbw-context-menu") || !o.subMenu) return;
|
|
196
|
+
const m = b(o.subMenu, e), a = x(m, e, n, i);
|
|
197
|
+
a.classList.add("tbw-context-submenu"), a.style.position = "absolute", a.style.left = "100%", a.style.top = "0", t.style.position = "relative", t.appendChild(a);
|
|
198
|
+
}), t.addEventListener("mouseleave", () => {
|
|
199
|
+
const u = t.querySelector(".tbw-context-menu");
|
|
200
|
+
u && u.remove();
|
|
143
201
|
});
|
|
144
202
|
}
|
|
145
|
-
!
|
|
146
|
-
|
|
147
|
-
}),
|
|
203
|
+
!c && o.action && !o.subMenu && t.addEventListener("click", (r) => {
|
|
204
|
+
r.stopPropagation(), n(o);
|
|
205
|
+
}), l.appendChild(t);
|
|
148
206
|
}
|
|
149
|
-
return
|
|
207
|
+
return l;
|
|
150
208
|
}
|
|
151
|
-
function E(
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
let
|
|
155
|
-
e +
|
|
209
|
+
function E(s, e, n) {
|
|
210
|
+
s.style.position = "fixed", s.style.left = `${e}px`, s.style.top = `${n}px`, s.style.visibility = "hidden", s.style.zIndex = "10000";
|
|
211
|
+
const i = s.getBoundingClientRect(), l = window.innerWidth, o = window.innerHeight;
|
|
212
|
+
let t = e, c = n;
|
|
213
|
+
e + i.width > l && (t = e - i.width), n + i.height > o && (c = n - i.height), t = Math.max(0, t), c = Math.max(0, c), s.style.left = `${t}px`, s.style.top = `${c}px`, s.style.visibility = "visible";
|
|
156
214
|
}
|
|
157
|
-
let f = null, p = null,
|
|
158
|
-
const
|
|
215
|
+
let f = null, p = null, h = null;
|
|
216
|
+
const I = `
|
|
159
217
|
.tbw-context-menu {
|
|
160
218
|
position: fixed;
|
|
161
219
|
background: light-dark(#f5f5f5, #2a2a2a);
|
|
@@ -206,31 +264,31 @@ const C = `
|
|
|
206
264
|
background: light-dark(#d0d0d4, #454545);
|
|
207
265
|
margin: 4px 0;
|
|
208
266
|
}
|
|
209
|
-
`,
|
|
267
|
+
`, g = [
|
|
210
268
|
{
|
|
211
269
|
id: "copy",
|
|
212
270
|
name: "Copy",
|
|
213
271
|
shortcut: "Ctrl+C",
|
|
214
|
-
action: (
|
|
215
|
-
|
|
272
|
+
action: (s) => {
|
|
273
|
+
s.grid?.plugins?.clipboard?.copy?.();
|
|
216
274
|
}
|
|
217
275
|
},
|
|
218
276
|
{ separator: !0, id: "sep1", name: "" },
|
|
219
277
|
{
|
|
220
278
|
id: "export-csv",
|
|
221
279
|
name: "Export CSV",
|
|
222
|
-
action: (
|
|
223
|
-
|
|
280
|
+
action: (s) => {
|
|
281
|
+
s.grid?.plugins?.export?.exportCsv?.();
|
|
224
282
|
}
|
|
225
283
|
}
|
|
226
284
|
];
|
|
227
|
-
class M extends
|
|
285
|
+
class M extends y {
|
|
228
286
|
name = "contextMenu";
|
|
229
287
|
version = "1.0.0";
|
|
230
288
|
get defaultConfig() {
|
|
231
289
|
return {
|
|
232
290
|
enabled: !0,
|
|
233
|
-
items:
|
|
291
|
+
items: g
|
|
234
292
|
};
|
|
235
293
|
}
|
|
236
294
|
// ===== Internal State =====
|
|
@@ -247,10 +305,10 @@ class M extends v {
|
|
|
247
305
|
}
|
|
248
306
|
// ===== Private Methods =====
|
|
249
307
|
installGlobalHandlers() {
|
|
250
|
-
!
|
|
251
|
-
document.querySelectorAll(".tbw-context-menu").forEach((
|
|
308
|
+
!h && typeof document < "u" && (h = document.createElement("style"), h.id = "tbw-context-menu-styles", h.textContent = I, document.head.appendChild(h)), f || (f = () => {
|
|
309
|
+
document.querySelectorAll(".tbw-context-menu").forEach((n) => n.remove());
|
|
252
310
|
}, document.addEventListener("click", f)), p || (p = (e) => {
|
|
253
|
-
e.key === "Escape" && document.querySelectorAll(".tbw-context-menu").forEach((
|
|
311
|
+
e.key === "Escape" && document.querySelectorAll(".tbw-context-menu").forEach((i) => i.remove());
|
|
254
312
|
}, document.addEventListener("keydown", p));
|
|
255
313
|
}
|
|
256
314
|
// ===== Hooks =====
|
|
@@ -258,44 +316,49 @@ class M extends v {
|
|
|
258
316
|
if (!this.config.enabled) return;
|
|
259
317
|
const e = this.shadowRoot;
|
|
260
318
|
if (!e) return;
|
|
261
|
-
const
|
|
262
|
-
|
|
319
|
+
const n = e.children[0];
|
|
320
|
+
n && n.getAttribute("data-context-menu-bound") !== "true" && (n.setAttribute("data-context-menu-bound", "true"), n.addEventListener("contextmenu", (i) => {
|
|
263
321
|
if (!this.config.enabled) return;
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
let
|
|
268
|
-
if (
|
|
269
|
-
const
|
|
270
|
-
|
|
322
|
+
const l = i;
|
|
323
|
+
l.preventDefault();
|
|
324
|
+
const o = l.target, t = o.closest("[data-row][data-col]"), c = o.closest(".header-cell");
|
|
325
|
+
let d;
|
|
326
|
+
if (t) {
|
|
327
|
+
const u = parseInt(t.getAttribute("data-row") ?? "-1", 10), m = parseInt(t.getAttribute("data-col") ?? "-1", 10), a = this.columns[m], w = this.rows[u];
|
|
328
|
+
d = {
|
|
271
329
|
row: w,
|
|
272
|
-
rowIndex:
|
|
273
|
-
column:
|
|
274
|
-
columnIndex:
|
|
275
|
-
field:
|
|
276
|
-
value: w?.[
|
|
330
|
+
rowIndex: u,
|
|
331
|
+
column: a,
|
|
332
|
+
columnIndex: m,
|
|
333
|
+
field: a?.field ?? "",
|
|
334
|
+
value: w?.[a?.field] ?? null,
|
|
277
335
|
isHeader: !1,
|
|
278
|
-
event:
|
|
336
|
+
event: l
|
|
279
337
|
};
|
|
280
|
-
} else if (
|
|
281
|
-
const
|
|
282
|
-
|
|
338
|
+
} else if (c) {
|
|
339
|
+
const u = parseInt(c.getAttribute("data-col") ?? "-1", 10), m = this.columns[u];
|
|
340
|
+
d = {
|
|
283
341
|
row: null,
|
|
284
342
|
rowIndex: -1,
|
|
285
|
-
column:
|
|
286
|
-
columnIndex:
|
|
287
|
-
field:
|
|
343
|
+
column: m,
|
|
344
|
+
columnIndex: u,
|
|
345
|
+
field: m?.field ?? "",
|
|
288
346
|
value: null,
|
|
289
347
|
isHeader: !0,
|
|
290
|
-
event:
|
|
348
|
+
event: l
|
|
291
349
|
};
|
|
292
350
|
} else
|
|
293
351
|
return;
|
|
294
|
-
this.params =
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
352
|
+
this.params = d, this.position = { x: l.clientX, y: l.clientY };
|
|
353
|
+
const r = b(this.config.items ?? g, d);
|
|
354
|
+
r.length && (this.menuElement && this.menuElement.remove(), this.menuElement = x(
|
|
355
|
+
r,
|
|
356
|
+
d,
|
|
357
|
+
(u) => {
|
|
358
|
+
u.action && u.action(d), this.menuElement?.remove(), this.menuElement = null, this.isOpen = !1;
|
|
359
|
+
},
|
|
360
|
+
this.gridIcons.submenuArrow
|
|
361
|
+
), document.body.appendChild(this.menuElement), E(this.menuElement, l.clientX, l.clientY), this.isOpen = !0, this.emit("context-menu-open", { params: d, items: r }));
|
|
299
362
|
}));
|
|
300
363
|
}
|
|
301
364
|
// ===== Public API =====
|
|
@@ -305,20 +368,25 @@ class M extends v {
|
|
|
305
368
|
* @param y - Y coordinate
|
|
306
369
|
* @param params - Partial context menu parameters
|
|
307
370
|
*/
|
|
308
|
-
showMenu(e,
|
|
309
|
-
const
|
|
310
|
-
row:
|
|
311
|
-
rowIndex:
|
|
312
|
-
column:
|
|
313
|
-
columnIndex:
|
|
314
|
-
field:
|
|
315
|
-
value:
|
|
316
|
-
isHeader:
|
|
317
|
-
event:
|
|
318
|
-
},
|
|
319
|
-
this.menuElement && this.menuElement.remove(), this.menuElement = x(
|
|
320
|
-
|
|
321
|
-
|
|
371
|
+
showMenu(e, n, i) {
|
|
372
|
+
const l = {
|
|
373
|
+
row: i.row ?? null,
|
|
374
|
+
rowIndex: i.rowIndex ?? -1,
|
|
375
|
+
column: i.column ?? null,
|
|
376
|
+
columnIndex: i.columnIndex ?? -1,
|
|
377
|
+
field: i.field ?? "",
|
|
378
|
+
value: i.value ?? null,
|
|
379
|
+
isHeader: i.isHeader ?? !1,
|
|
380
|
+
event: i.event ?? new MouseEvent("contextmenu")
|
|
381
|
+
}, o = b(this.config.items ?? g, l);
|
|
382
|
+
this.menuElement && this.menuElement.remove(), this.menuElement = x(
|
|
383
|
+
o,
|
|
384
|
+
l,
|
|
385
|
+
(t) => {
|
|
386
|
+
t.action && t.action(l), this.menuElement?.remove(), this.menuElement = null, this.isOpen = !1;
|
|
387
|
+
},
|
|
388
|
+
this.gridIcons.submenuArrow
|
|
389
|
+
), document.body.appendChild(this.menuElement), E(this.menuElement, e, n), this.isOpen = !0;
|
|
322
390
|
}
|
|
323
391
|
/**
|
|
324
392
|
* Hide the context menu.
|