mcv-data-table 0.1.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mabbasi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # mcv-data-table
2
+
3
+ A Vue 3 + Vuetify 3 data table component based on `v-data-table` with column visibility, column resizing, and drag-and-drop column reordering.
4
+
5
+ ## Demo
6
+
7
+ https://mojtabaabbasi2023.github.io/MCVDataTable/
8
+
9
+ ## Requirements
10
+
11
+ - Vue `^3.5.0`
12
+ - Vuetify `3.11.3`
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install mcv-data-table
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ Register the plugin:
23
+
24
+ ```ts
25
+ import { createApp } from 'vue'
26
+ import App from './App.vue'
27
+ import MCVDataTablePlugin from 'mcv-data-table'
28
+ import 'mcv-data-table/style.css'
29
+
30
+ const app = createApp(App)
31
+
32
+ app.use(MCVDataTablePlugin)
33
+ app.mount('#app')
34
+ ```
35
+
36
+ Use the component:
37
+
38
+ ```vue
39
+ <template>
40
+ <MCVDataTable :headers="headers" :items="items" />
41
+ </template>
42
+ ```
43
+
44
+ Or import the component directly:
45
+
46
+ ```ts
47
+ import { MCVDataTable } from 'mcv-data-table'
48
+ import 'mcv-data-table/style.css'
49
+ ```
50
+
51
+ ## Header Props
52
+
53
+ Headers use the same props as Vuetify `v-data-table` headers. This package adds the following optional header props:
54
+
55
+ ```ts
56
+ type MCVDataTableHeader = {
57
+ group?: string
58
+ hide?: boolean
59
+ resizable?: boolean
60
+ }
61
+ ```
62
+
63
+ ### `resizable`
64
+
65
+ Set `resizable` to `false` to disable resizing for a specific column.
66
+
67
+ ```ts
68
+ { title: 'Amount', key: 'amount', resizable: false }
69
+ ```
70
+
71
+ ### `group`
72
+
73
+ Use `group` to group columns in the column visibility menu. Columns with the same group value are displayed together.
74
+
75
+ ```ts
76
+ { title: 'Status', key: 'status', group: 'workflow' }
77
+ { title: 'Owner', key: 'owner', group: 'workflow' }
78
+ ```
79
+
80
+ ### `hide`
81
+
82
+ Set `hide` to `true` to hide a column by default when the table is first rendered.
83
+
84
+ ```ts
85
+ { title: 'Amount', key: 'amount', hide: true }
86
+ ```
87
+
88
+ ## Notes
89
+
90
+ This package expects Vuetify to be installed and configured in the consuming Vue application.
91
+
92
+ ## License
93
+
94
+ MIT
@@ -0,0 +1,50 @@
1
+ import { DataTableHeader } from 'vuetify';
2
+ type DataTableItem = Record<string, any>;
3
+ interface GenericTableColumn extends DataTableHeader {
4
+ key: string;
5
+ group?: string | number;
6
+ disabled?: boolean;
7
+ hide?: boolean;
8
+ resizable?: boolean;
9
+ }
10
+ type RowPropsContext = {
11
+ item: DataTableItem;
12
+ index: number;
13
+ };
14
+ interface Props {
15
+ items: DataTableItem[];
16
+ headers: GenericTableColumn[];
17
+ pageSize?: number;
18
+ resizeDirection?: 'rtl' | 'ltr';
19
+ loading?: boolean;
20
+ itemValue?: string;
21
+ activeItemValue?: string | number;
22
+ fixedHeaders?: GenericTableColumn[];
23
+ fixedEndHeaders?: GenericTableColumn[];
24
+ tableClass?: string;
25
+ showExpand?: boolean;
26
+ singleExpand?: boolean;
27
+ rowProps?: (context: RowPropsContext) => Record<string, any>;
28
+ }
29
+ declare function __VLS_template(): any;
30
+ type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
31
+ declare const __VLS_component: import('vue').DefineComponent<Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<Props> & Readonly<{}>, {
32
+ headers: GenericTableColumn[];
33
+ items: DataTableItem[];
34
+ pageSize: number;
35
+ resizeDirection: "rtl" | "ltr";
36
+ loading: boolean;
37
+ itemValue: string;
38
+ fixedHeaders: GenericTableColumn[];
39
+ fixedEndHeaders: GenericTableColumn[];
40
+ tableClass: string;
41
+ showExpand: boolean;
42
+ singleExpand: boolean;
43
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, any, any>;
44
+ declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
45
+ export default _default;
46
+ type __VLS_WithTemplateSlots<T, S> = T & {
47
+ new (): {
48
+ $slots: S;
49
+ };
50
+ };
@@ -0,0 +1,14 @@
1
+ import { DataTableHeader } from 'vuetify';
2
+ interface UseDataTableResizeOptions {
3
+ tableRef: Ref;
4
+ headers: Ref<DataTableHeader[]>;
5
+ minWidth?: number;
6
+ defaultWidth?: number;
7
+ direction?: 'rtl' | 'ltr';
8
+ }
9
+ export declare function useDataTableResize({ tableRef, headers, minWidth, defaultWidth, direction }: UseDataTableResizeOptions): {
10
+ startResize: (event: MouseEvent, index: number) => void;
11
+ getTable: () => any;
12
+ tableWidth: import('vue').ComputedRef<number>;
13
+ };
14
+ export {};
@@ -0,0 +1,11 @@
1
+ import { Ref } from 'vue';
2
+ import { DataTableHeader } from 'vuetify';
3
+ type TableColumnWithVisibility = DataTableHeader & {
4
+ key: string;
5
+ hide?: boolean;
6
+ };
7
+ export declare function useTableColumns<T extends TableColumnWithVisibility = TableColumnWithVisibility>(fixedHeaders: T[], toggleHeaders: Ref<T[]>, fixedEndHeaders: T[], tableRef?: Ref, defaultWidth?: number): {
8
+ visibleColumns: Ref<string[], string[]>;
9
+ filteredHeaders: import('vue').ComputedRef<T[]>;
10
+ };
11
+ export {};
@@ -0,0 +1,7 @@
1
+ import { App } from 'vue';
2
+ import { default as MCVDataTable } from './components/MCVdataTable.vue';
3
+ export { MCVDataTable };
4
+ declare const _default: {
5
+ install(app: App): void;
6
+ };
7
+ export default _default;
@@ -0,0 +1,353 @@
1
+ import { ref as _, computed as R, watch as U, nextTick as P, onMounted as Z, defineComponent as ee, useSlots as te, resolveComponent as k, openBlock as E, createBlock as B, unref as u, normalizeClass as I, createSlots as ne, withCtx as x, createElementVNode as W, renderSlot as A, renderList as N, createElementBlock as H, createVNode as $, Fragment as ae, createCommentVNode as q, isRef as re, mergeProps as oe, createTextVNode as le, withModifiers as se, toDisplayString as X, normalizeProps as ie, guardReactiveProps as de } from "vue";
2
+ function ue(a, t, y, c, v = 200) {
3
+ const i = _(
4
+ t.value.filter((o) => !o.hide).map((o) => o.key)
5
+ ), d = R(() => {
6
+ const o = t.value.filter(
7
+ (m) => i.value.includes(m.key)
8
+ );
9
+ return [...a, ...o, ...y];
10
+ });
11
+ function b() {
12
+ var m, p;
13
+ const o = (p = (m = c == null ? void 0 : c.value) == null ? void 0 : m.$el) == null ? void 0 : p.querySelector("table");
14
+ return Array.from(
15
+ (o == null ? void 0 : o.querySelectorAll("thead th")) ?? []
16
+ );
17
+ }
18
+ function z() {
19
+ const o = b();
20
+ o.length && d.value.forEach((m, p) => {
21
+ var e;
22
+ if (m.width != null)
23
+ return;
24
+ (((e = o[p]) == null ? void 0 : e.getBoundingClientRect().width) ?? v) < v && (m.width = v);
25
+ });
26
+ }
27
+ return U(
28
+ () => [...i.value],
29
+ async () => {
30
+ await P(), z();
31
+ },
32
+ { flush: "post" }
33
+ ), {
34
+ visibleColumns: i,
35
+ filteredHeaders: d
36
+ };
37
+ }
38
+ function ce({
39
+ tableRef: a,
40
+ headers: t,
41
+ minWidth: y = 80,
42
+ defaultWidth: c = 120,
43
+ direction: v = "rtl"
44
+ }) {
45
+ const i = _(!1), d = R(() => t.value.reduce((e, s) => {
46
+ const w = typeof s.width == "number" ? s.width : Number.parseFloat(s.width ?? "0");
47
+ return e + (Number.isFinite(w) ? w : 0);
48
+ }, 0));
49
+ function b() {
50
+ var e, s;
51
+ return (s = (e = a.value) == null ? void 0 : e.$el) == null ? void 0 : s.querySelector("table");
52
+ }
53
+ function z() {
54
+ const e = b();
55
+ return Array.from(
56
+ (e == null ? void 0 : e.querySelectorAll("thead th")) ?? []
57
+ );
58
+ }
59
+ function o() {
60
+ const e = z();
61
+ t.value.forEach((s, w) => {
62
+ var S;
63
+ if (s.width != null)
64
+ return;
65
+ const D = (S = e[w]) == null ? void 0 : S.getBoundingClientRect().width;
66
+ s.width = Math.max(
67
+ y,
68
+ Math.round(D || c)
69
+ );
70
+ });
71
+ }
72
+ function m() {
73
+ o(), i.value = !0, p();
74
+ }
75
+ function p() {
76
+ const e = b();
77
+ !e || !i.value || (e.style.width = d.value + "px");
78
+ }
79
+ function f(e, s) {
80
+ e.preventDefault(), m();
81
+ const w = e.pageX, D = t.value[s].width, S = typeof D == "number" ? D : Number.parseFloat(D ?? `${c}`);
82
+ function M(r) {
83
+ const T = v === "rtl" ? w - r.pageX : r.pageX - w;
84
+ t.value[s].width = Math.max(
85
+ y,
86
+ S + T
87
+ ), p();
88
+ }
89
+ function n() {
90
+ document.removeEventListener(
91
+ "mousemove",
92
+ M
93
+ ), document.removeEventListener(
94
+ "mouseup",
95
+ n
96
+ );
97
+ }
98
+ document.addEventListener(
99
+ "mousemove",
100
+ M
101
+ ), document.addEventListener(
102
+ "mouseup",
103
+ n
104
+ );
105
+ }
106
+ return Z(() => {
107
+ P(() => {
108
+ p();
109
+ });
110
+ }), U(
111
+ () => t.value.map((e) => e.key),
112
+ () => {
113
+ P(() => {
114
+ i.value && o(), p();
115
+ });
116
+ }
117
+ ), {
118
+ startResize: f,
119
+ getTable: b,
120
+ tableWidth: d
121
+ };
122
+ }
123
+ function fe(a, t, y) {
124
+ if (!t || t === y)
125
+ return;
126
+ const c = a.findIndex(
127
+ (d) => d.key === t
128
+ ), v = a.findIndex(
129
+ (d) => d.key === y
130
+ );
131
+ if (c === -1 || v === -1)
132
+ return;
133
+ const [i] = a.splice(c, 1);
134
+ a.splice(v, 0, i);
135
+ }
136
+ const ve = {
137
+ key: 0,
138
+ class: "d-flex align-center justify-space-between w-100"
139
+ }, me = ["onDragstart", "onDragover", "onDragenter", "onDragleave", "onDrop"], pe = ["onMousedown"], ge = { class: "mc-vdata-table__row-number" }, he = ["colspan"], ye = /* @__PURE__ */ ee({
140
+ __name: "MCVdataTable",
141
+ props: {
142
+ items: { default: () => [] },
143
+ headers: { default: () => [] },
144
+ pageSize: { default: 20 },
145
+ resizeDirection: { default: "rtl" },
146
+ loading: { type: Boolean, default: !1 },
147
+ itemValue: { default: "id" },
148
+ activeItemValue: {},
149
+ fixedHeaders: { default: () => [{ key: "data-table-expand", width: 69, resizable: !1 }] },
150
+ fixedEndHeaders: { default: () => [] },
151
+ tableClass: { default: "elevation-1 report-table" },
152
+ showExpand: { type: Boolean, default: !1 },
153
+ singleExpand: { type: Boolean, default: !0 },
154
+ rowProps: {}
155
+ },
156
+ setup(a) {
157
+ const t = a, y = _([...t.headers]), c = _([...t.headers]), v = _([]), i = _(null), { visibleColumns: d, filteredHeaders: b } = ue(
158
+ t.fixedHeaders,
159
+ c,
160
+ t.fixedEndHeaders,
161
+ i
162
+ ), z = te(), o = R(
163
+ () => Object.keys(z).filter(
164
+ (n) => n.startsWith("item.")
165
+ )
166
+ ), m = (n) => {
167
+ var r;
168
+ return t.rowProps ? t.rowProps(n) : {
169
+ class: t.activeItemValue !== void 0 && ((r = n.item) == null ? void 0 : r[t.itemValue]) === t.activeItemValue ? "active" : ""
170
+ };
171
+ }, { startResize: p } = ce({
172
+ tableRef: i,
173
+ headers: b,
174
+ minWidth: 80,
175
+ defaultWidth: 120,
176
+ direction: t.resizeDirection
177
+ }), f = _(), e = _(null);
178
+ function s(n, r, T) {
179
+ if (f.value = r, !n.dataTransfer) return;
180
+ n.dataTransfer.effectAllowed = "move", n.dataTransfer.setData("text/plain", r);
181
+ const C = document.createElement("div");
182
+ C.className = "mc-vdata-table__drag-preview", C.textContent = String(T || r), document.body.appendChild(C), n.dataTransfer.setDragImage(C, 14, 16), window.setTimeout(() => C.remove(), 0);
183
+ }
184
+ function w(n) {
185
+ f.value && f.value !== n && (e.value = n);
186
+ }
187
+ function D(n) {
188
+ e.value === n && (e.value = null);
189
+ }
190
+ function S() {
191
+ f.value = null, e.value = null;
192
+ }
193
+ function M(n) {
194
+ const r = f.value;
195
+ if (r)
196
+ try {
197
+ fe(
198
+ c.value,
199
+ r,
200
+ n
201
+ );
202
+ } finally {
203
+ f.value = null, e.value = null;
204
+ }
205
+ }
206
+ return (n, r) => {
207
+ const T = k("v-icon"), C = k("v-btn"), j = k("v-divider"), O = k("v-checkbox"), G = k("v-list-item"), J = k("v-list"), K = k("v-menu"), Q = k("v-data-table");
208
+ return E(), B(Q, {
209
+ headers: u(b),
210
+ items: a.items,
211
+ "hide-default-footer": "",
212
+ "items-per-page": a.pageSize,
213
+ class: I(`mc-vdata-table ${a.tableClass}`),
214
+ "show-expand": a.showExpand,
215
+ "single-expand": a.singleExpand,
216
+ expanded: u(v),
217
+ "item-value": a.itemValue,
218
+ "onUpdate:expanded": r[1] || (r[1] = (g) => v.value = g),
219
+ "fixed-header": "",
220
+ loading: a.loading,
221
+ "row-props": m,
222
+ ref_key: "tableRef",
223
+ ref: i,
224
+ "disable-sort": ""
225
+ }, ne({
226
+ "expanded-row": x(({ columns: g, item: V }) => [
227
+ W("tr", null, [
228
+ W("td", {
229
+ colspan: g.length,
230
+ class: "py-2 bg-surface-light"
231
+ }, [
232
+ A(n.$slots, "expanded-row", {
233
+ columns: g,
234
+ item: V
235
+ })
236
+ ], 8, he)
237
+ ])
238
+ ]),
239
+ _: 2
240
+ }, [
241
+ N(u(b), (g, V) => ({
242
+ name: `header.${g.key}`,
243
+ fn: x(({ column: h }) => {
244
+ var F;
245
+ return [
246
+ h.key === "data-table-expand" ? (E(), H("div", ve, [
247
+ $(K, {
248
+ "offset-y": "",
249
+ "close-on-content-click": !1,
250
+ "scroll-strategy": "none"
251
+ }, {
252
+ activator: x(({ props: l }) => [
253
+ $(C, oe(l, {
254
+ icon: "",
255
+ variant: "text"
256
+ }), {
257
+ default: x(() => [
258
+ $(T, null, {
259
+ default: x(() => [...r[2] || (r[2] = [
260
+ le("mdi-eye-settings", -1)
261
+ ])]),
262
+ _: 1
263
+ })
264
+ ]),
265
+ _: 1
266
+ }, 16)
267
+ ]),
268
+ default: x(() => [
269
+ $(J, {
270
+ class: "vlist-small",
271
+ height: 300
272
+ }, {
273
+ default: x(() => [
274
+ (E(!0), H(ae, null, N(u(y), (l, L) => (E(), B(G, {
275
+ key: l.key
276
+ }, {
277
+ default: x(() => [
278
+ L > 0 && u(y)[L - 1].group !== l.group ? (E(), B(j, {
279
+ key: 0,
280
+ class: "my-2"
281
+ })) : q("", !0),
282
+ $(O, {
283
+ modelValue: u(d),
284
+ "onUpdate:modelValue": r[0] || (r[0] = (Y) => re(d) ? d.value = Y : null),
285
+ label: l.title,
286
+ value: l.key,
287
+ "hide-details": "",
288
+ density: "compact",
289
+ disabled: l.disabled
290
+ }, null, 8, ["modelValue", "label", "value", "disabled"])
291
+ ]),
292
+ _: 2
293
+ }, 1024))), 128))
294
+ ]),
295
+ _: 2
296
+ }, 1024)
297
+ ]),
298
+ _: 2
299
+ }, 1024)
300
+ ])) : (E(), H("div", {
301
+ key: 1,
302
+ class: I([
303
+ "header-content",
304
+ `header-content--indicator-${t.resizeDirection}`,
305
+ {
306
+ "header-content--dragging": u(f) === h.key,
307
+ "header-content--drop-target": u(f) && u(f) !== h.key,
308
+ "header-content--drag-over": u(e) === h.key
309
+ }
310
+ ]),
311
+ draggable: "true",
312
+ onDragstart: (l) => s(l, String(h.key), h.title),
313
+ onDragover: se((l) => w(String(h.key)), ["prevent"]),
314
+ onDragenter: (l) => w(String(h.key)),
315
+ onDragleave: (l) => D(String(h.key)),
316
+ onDrop: (l) => M(String(h.key)),
317
+ onDragend: S
318
+ }, [
319
+ W("span", null, X(h.title), 1),
320
+ ((F = u(b)[V]) == null ? void 0 : F.resizable) !== !1 ? (E(), H("span", {
321
+ key: 0,
322
+ class: I(["resize-handle", `resize-handle--${t.resizeDirection}`]),
323
+ onMousedown: (l) => u(p)(l, V)
324
+ }, null, 42, pe)) : q("", !0)
325
+ ], 42, me))
326
+ ];
327
+ })
328
+ })),
329
+ N(u(o), (g) => ({
330
+ name: g,
331
+ fn: x((V) => [
332
+ A(n.$slots, g, ie(de(V)))
333
+ ])
334
+ })),
335
+ a.showExpand ? void 0 : {
336
+ name: "item.data-table-expand",
337
+ fn: x(({ index: g }) => [
338
+ W("span", ge, X(g + 1), 1)
339
+ ]),
340
+ key: "0"
341
+ }
342
+ ]), 1032, ["headers", "items", "items-per-page", "class", "show-expand", "single-expand", "expanded", "item-value", "loading"]);
343
+ };
344
+ }
345
+ }), we = {
346
+ install(a) {
347
+ a.component("MCVDataTable", ye);
348
+ }
349
+ };
350
+ export {
351
+ ye as MCVDataTable,
352
+ we as default
353
+ };
@@ -0,0 +1 @@
1
+ (function(C,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue")):typeof define=="function"&&define.amd?define(["exports","vue"],e):(C=typeof globalThis<"u"?globalThis:C||self,e(C.MCVDataTable={},C.Vue))})(this,function(C,e){"use strict";function v(o,n,y,f,p=200){const d=e.ref(n.value.filter(l=>!l.hide).map(l=>l.key)),c=e.computed(()=>{const l=n.value.filter(u=>d.value.includes(u.key));return[...o,...l,...y]});function b(){var u,h;const l=(h=(u=f==null?void 0:f.value)==null?void 0:u.$el)==null?void 0:h.querySelector("table");return Array.from((l==null?void 0:l.querySelectorAll("thead th"))??[])}function V(){const l=b();l.length&&c.value.forEach((u,h)=>{var t;if(u.width!=null)return;(((t=l[h])==null?void 0:t.getBoundingClientRect().width)??p)<p&&(u.width=p)})}return e.watch(()=>[...d.value],async()=>{await e.nextTick(),V()},{flush:"post"}),{visibleColumns:d,filteredHeaders:c}}function M({tableRef:o,headers:n,minWidth:y=80,defaultWidth:f=120,direction:p="rtl"}){const d=e.ref(!1),c=e.computed(()=>n.value.reduce((t,s)=>{const x=typeof s.width=="number"?s.width:Number.parseFloat(s.width??"0");return t+(Number.isFinite(x)?x:0)},0));function b(){var t,s;return(s=(t=o.value)==null?void 0:t.$el)==null?void 0:s.querySelector("table")}function V(){const t=b();return Array.from((t==null?void 0:t.querySelectorAll("thead th"))??[])}function l(){const t=V();n.value.forEach((s,x)=>{var D;if(s.width!=null)return;const k=(D=t[x])==null?void 0:D.getBoundingClientRect().width;s.width=Math.max(y,Math.round(k||f))})}function u(){l(),d.value=!0,h()}function h(){const t=b();!t||!d.value||(t.style.width=c.value+"px")}function m(t,s){t.preventDefault(),u();const x=t.pageX,k=n.value[s].width,D=typeof k=="number"?k:Number.parseFloat(k??`${f}`);function T(r){const S=p==="rtl"?x-r.pageX:r.pageX-x;n.value[s].width=Math.max(y,D+S),h()}function a(){document.removeEventListener("mousemove",T),document.removeEventListener("mouseup",a)}document.addEventListener("mousemove",T),document.addEventListener("mouseup",a)}return e.onMounted(()=>{e.nextTick(()=>{h()})}),e.watch(()=>n.value.map(t=>t.key),()=>{e.nextTick(()=>{d.value&&l(),h()})}),{startResize:m,getTable:b,tableWidth:c}}function $(o,n,y){if(!n||n===y)return;const f=o.findIndex(c=>c.key===n),p=o.findIndex(c=>c.key===y);if(f===-1||p===-1)return;const[d]=o.splice(f,1);o.splice(p,0,d)}const W={key:0,class:"d-flex align-center justify-space-between w-100"},H=["onDragstart","onDragover","onDragenter","onDragleave","onDrop"],P=["onMousedown"],I={class:"mc-vdata-table__row-number"},L=["colspan"],B=e.defineComponent({__name:"MCVdataTable",props:{items:{default:()=>[]},headers:{default:()=>[]},pageSize:{default:20},resizeDirection:{default:"rtl"},loading:{type:Boolean,default:!1},itemValue:{default:"id"},activeItemValue:{},fixedHeaders:{default:()=>[{key:"data-table-expand",width:69,resizable:!1}]},fixedEndHeaders:{default:()=>[]},tableClass:{default:"elevation-1 report-table"},showExpand:{type:Boolean,default:!1},singleExpand:{type:Boolean,default:!0},rowProps:{}},setup(o){const n=o,y=e.ref([...n.headers]),f=e.ref([...n.headers]),p=e.ref([]),d=e.ref(null),{visibleColumns:c,filteredHeaders:b}=v(n.fixedHeaders,f,n.fixedEndHeaders,d),V=e.useSlots(),l=e.computed(()=>Object.keys(V).filter(a=>a.startsWith("item."))),u=a=>{var r;return n.rowProps?n.rowProps(a):{class:n.activeItemValue!==void 0&&((r=a.item)==null?void 0:r[n.itemValue])===n.activeItemValue?"active":""}},{startResize:h}=M({tableRef:d,headers:b,minWidth:80,defaultWidth:120,direction:n.resizeDirection}),m=e.ref(),t=e.ref(null);function s(a,r,S){if(m.value=r,!a.dataTransfer)return;a.dataTransfer.effectAllowed="move",a.dataTransfer.setData("text/plain",r);const _=document.createElement("div");_.className="mc-vdata-table__drag-preview",_.textContent=String(S||r),document.body.appendChild(_),a.dataTransfer.setDragImage(_,14,16),window.setTimeout(()=>_.remove(),0)}function x(a){m.value&&m.value!==a&&(t.value=a)}function k(a){t.value===a&&(t.value=null)}function D(){m.value=null,t.value=null}function T(a){const r=m.value;if(r)try{$(f.value,r,a)}finally{m.value=null,t.value=null}}return(a,r)=>{const S=e.resolveComponent("v-icon"),_=e.resolveComponent("v-btn"),F=e.resolveComponent("v-divider"),q=e.resolveComponent("v-checkbox"),A=e.resolveComponent("v-list-item"),j=e.resolveComponent("v-list"),X=e.resolveComponent("v-menu"),O=e.resolveComponent("v-data-table");return e.openBlock(),e.createBlock(O,{headers:e.unref(b),items:o.items,"hide-default-footer":"","items-per-page":o.pageSize,class:e.normalizeClass(`mc-vdata-table ${o.tableClass}`),"show-expand":o.showExpand,"single-expand":o.singleExpand,expanded:e.unref(p),"item-value":o.itemValue,"onUpdate:expanded":r[1]||(r[1]=g=>p.value=g),"fixed-header":"",loading:o.loading,"row-props":u,ref_key:"tableRef",ref:d,"disable-sort":""},e.createSlots({"expanded-row":e.withCtx(({columns:g,item:E})=>[e.createElementVNode("tr",null,[e.createElementVNode("td",{colspan:g.length,class:"py-2 bg-surface-light"},[e.renderSlot(a.$slots,"expanded-row",{columns:g,item:E})],8,L)])]),_:2},[e.renderList(e.unref(b),(g,E)=>({name:`header.${g.key}`,fn:e.withCtx(({column:w})=>{var z;return[w.key==="data-table-expand"?(e.openBlock(),e.createElementBlock("div",W,[e.createVNode(X,{"offset-y":"","close-on-content-click":!1,"scroll-strategy":"none"},{activator:e.withCtx(({props:i})=>[e.createVNode(_,e.mergeProps(i,{icon:"",variant:"text"}),{default:e.withCtx(()=>[e.createVNode(S,null,{default:e.withCtx(()=>[...r[2]||(r[2]=[e.createTextVNode("mdi-eye-settings",-1)])]),_:1})]),_:1},16)]),default:e.withCtx(()=>[e.createVNode(j,{class:"vlist-small",height:300},{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(y),(i,N)=>(e.openBlock(),e.createBlock(A,{key:i.key},{default:e.withCtx(()=>[N>0&&e.unref(y)[N-1].group!==i.group?(e.openBlock(),e.createBlock(F,{key:0,class:"my-2"})):e.createCommentVNode("",!0),e.createVNode(q,{modelValue:e.unref(c),"onUpdate:modelValue":r[0]||(r[0]=U=>e.isRef(c)?c.value=U:null),label:i.title,value:i.key,"hide-details":"",density:"compact",disabled:i.disabled},null,8,["modelValue","label","value","disabled"])]),_:2},1024))),128))]),_:2},1024)]),_:2},1024)])):(e.openBlock(),e.createElementBlock("div",{key:1,class:e.normalizeClass(["header-content",`header-content--indicator-${n.resizeDirection}`,{"header-content--dragging":e.unref(m)===w.key,"header-content--drop-target":e.unref(m)&&e.unref(m)!==w.key,"header-content--drag-over":e.unref(t)===w.key}]),draggable:"true",onDragstart:i=>s(i,String(w.key),w.title),onDragover:e.withModifiers(i=>x(String(w.key)),["prevent"]),onDragenter:i=>x(String(w.key)),onDragleave:i=>k(String(w.key)),onDrop:i=>T(String(w.key)),onDragend:D},[e.createElementVNode("span",null,e.toDisplayString(w.title),1),((z=e.unref(b)[E])==null?void 0:z.resizable)!==!1?(e.openBlock(),e.createElementBlock("span",{key:0,class:e.normalizeClass(["resize-handle",`resize-handle--${n.resizeDirection}`]),onMousedown:i=>e.unref(h)(i,E)},null,42,P)):e.createCommentVNode("",!0)],42,H))]})})),e.renderList(e.unref(l),g=>({name:g,fn:e.withCtx(E=>[e.renderSlot(a.$slots,g,e.normalizeProps(e.guardReactiveProps(E)))])})),o.showExpand?void 0:{name:"item.data-table-expand",fn:e.withCtx(({index:g})=>[e.createElementVNode("span",I,e.toDisplayString(g+1),1)]),key:"0"}]),1032,["headers","items","items-per-page","class","show-expand","single-expand","expanded","item-value","loading"])}}}),R={install(o){o.component("MCVDataTable",B)}};C.MCVDataTable=B,C.default=R,Object.defineProperties(C,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ .mc-vdata-table.v-data-table .v-table__wrapper table{table-layout:fixed}.mc-vdata-table.v-data-table .v-table__wrapper table tr.active{background-color:rgba(var(--v-theme-primary),.2)}.mc-vdata-table.v-data-table .v-table__wrapper table td,.mc-vdata-table.v-data-table .v-table__wrapper table th{border-right:thin solid rgba(var(--v-border-color),var(--v-border-opacity))}.mc-vdata-table.v-data-table .v-table__wrapper table thead th{font-weight:700!important;text-align:center!important}.mc-vdata-table.v-data-table .v-table__wrapper table thead th:not(.v-data-table-column--fixed){position:relative!important}.mc-vdata-table.v-data-table .v-table__wrapper table thead th .v-data-table-header__content{justify-content:center}.mc-vdata-table.v-data-table .v-table__wrapper table thead th .header-content{cursor:pointer}.mc-vdata-table.v-data-table .v-table__wrapper table thead th .header-content:before{background:rgb(var(--v-theme-primary));bottom:5px;content:"";height:22px;opacity:0;position:absolute;top:50%;transform:translateY(-50%) scaleY(.72);transition:opacity .18s ease,transform .18s ease;width:2px}.mc-vdata-table.v-data-table .v-table__wrapper table thead th .header-content--drop-target:hover{background:rgba(var(--v-theme-on-surface),.025)}.mc-vdata-table.v-data-table .v-table__wrapper table thead th .header-content--drag-over{background:rgba(var(--v-theme-on-surface),.025);border-color:rgba(var(--v-border-color),.18);box-shadow:none}.mc-vdata-table.v-data-table .v-table__wrapper table thead th .header-content--drag-over:before{opacity:1;transform:translateY(-50%) scaleY(1)}.mc-vdata-table.v-data-table .v-table__wrapper table thead th .header-content--indicator-ltr:before{left:-1px}.mc-vdata-table.v-data-table .v-table__wrapper table thead th .header-content--indicator-rtl:before{right:-1px}.mc-vdata-table .vlist-small .v-list-item{padding:0 5px!important;min-block-size:30px!important}.mc-vdata-table .vlist-small .v-list-item .v-list-item-title{font-size:1em}.mc-vdata-table .resize-handle{position:absolute;top:0;bottom:0;width:6px;cursor:col-resize;-webkit-user-select:none;user-select:none}.mc-vdata-table .resize-handle--rtl{left:0}.mc-vdata-table .resize-handle--ltr{right:0}.mc-vdata-table .resize-handle:hover{border-left:2px solid blue}.mc-vdata-table .resize-handle--ltr:hover{border-left:0;border-right:2px solid blue}.mc-vdata-table__drag-preview{align-items:center;background:rgb(var(--v-theme-surface));border:1px solid rgba(var(--v-theme-primary),.45);border-radius:4px;box-shadow:0 6px 18px #0000002e;color:rgba(var(--v-theme-on-surface),.9);display:inline-flex;font-size:12px;font-weight:600;line-height:1;max-width:180px;overflow:hidden;padding:8px 12px;pointer-events:none;position:fixed;text-overflow:ellipsis;top:-1000px;white-space:nowrap;z-index:9999}
@@ -0,0 +1,3 @@
1
+ export declare function moveColumn<T extends {
2
+ key: string;
3
+ }>(columns: T[], sourceKey: string, targetKey: string): void;
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "mcv-data-table",
3
+ "version": "0.1.3",
4
+ "description": "A Vuetify 3 data table component with column visibility, resizing, and drag-and-drop column reordering.",
5
+ "type": "module",
6
+ "main": "./dist/mcv-data-table.umd.cjs",
7
+ "module": "./dist/mcv-data-table.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/mcv-data-table.js",
18
+ "require": "./dist/mcv-data-table.umd.cjs"
19
+ },
20
+ "./style.css": "./dist/style.css"
21
+ },
22
+ "scripts": {
23
+ "build": "vue-tsc --noEmit && vite build",
24
+ "build:watch": "vite build --watch",
25
+ "dev:types": "vue-tsc --noEmit --watch",
26
+ "pack:local": "npm run build && npm pack",
27
+ "release:patch": "npm version patch && npm run build && npm pack",
28
+ "release:minor": "npm version minor && npm run build && npm pack",
29
+ "release:major": "npm version major && npm run build && npm pack",
30
+ "prepublishOnly": "npm run build"
31
+ },
32
+ "peerDependencies": {
33
+ "vue": "^3.5.0",
34
+ "vuetify": "3.11.3"
35
+ },
36
+ "devDependencies": {
37
+ "@vitejs/plugin-vue": "^5.2.0",
38
+ "@types/node": "^22.0.0",
39
+ "sass": "^1.77.0",
40
+ "typescript": "^5.5.0",
41
+ "unplugin-auto-import": "^0.18.6",
42
+ "vite": "^5.4.0",
43
+ "vite-plugin-dts": "^4.5.0",
44
+ "vue": "^3.5.0",
45
+ "vue-tsc": "^2.0.0",
46
+ "vuetify": "3.11.3"
47
+ },
48
+ "keywords": [
49
+ "vue",
50
+ "vue3",
51
+ "vuetify",
52
+ "v-data-table",
53
+ "data-table",
54
+ "typescript"
55
+ ],
56
+ "license": "MIT"
57
+ }