@oneflowui/ui 0.4.3 → 0.4.4
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/dist/assets/tableWorker-CTsbCPPP.js +1 -0
- package/dist/components/ContextMenu/index.vue.d.ts +2 -1
- package/dist/components/ContextMenu/index.vue.js +2 -2
- package/dist/components/ContextMenu/index.vue2.js +37 -36
- package/dist/components/base/DropdownMenu.vue.js +2 -2
- package/dist/components/base/DropdownMenu.vue2.js +38 -39
- package/dist/components/base/MonitorItem.vue.d.ts +1 -1
- package/dist/components/base/PersonaCard.vue.d.ts +2 -1
- package/dist/components/base/PersonaCard.vue.js +3 -3
- package/dist/components/base/PersonaCard.vue2.js +51 -39
- package/dist/components/base/RefTag.vue.d.ts +2 -2
- package/dist/components/base/RefTag.vue.js +3 -3
- package/dist/components/base/SearchHighlight.vue.d.ts +6 -0
- package/dist/components/base/SearchHighlight.vue.js +7 -0
- package/dist/components/base/SearchHighlight.vue2.js +21 -0
- package/dist/components/base/ToolbarBtn.vue.d.ts +2 -1
- package/dist/components/base/ToolbarBtn.vue.js +1 -1
- package/dist/components/base/ViewModeGroup.vue.d.ts +2 -1
- package/dist/components/base/ViewModeGroup.vue.js +3 -3
- package/dist/components/base/ViewSwitcher.vue.d.ts +2 -1
- package/dist/components/base/ViewSwitcher.vue.js +2 -2
- package/dist/components/base/index.d.ts +1 -0
- package/dist/components/field/FieldAttachment.vue.d.ts +17 -0
- package/dist/components/field/FieldAttachment.vue.js +7 -0
- package/dist/components/field/FieldAttachment.vue2.js +69 -0
- package/dist/components/field/FieldAutoNumber.vue.d.ts +7 -0
- package/dist/components/field/FieldAutoNumber.vue.js +7 -0
- package/dist/components/field/FieldAutoNumber.vue2.js +15 -0
- package/dist/components/field/FieldCreator.vue.d.ts +7 -0
- package/dist/components/field/FieldCreator.vue.js +7 -0
- package/dist/components/field/FieldCreator.vue2.js +24 -0
- package/dist/components/field/FieldCurrency.vue.d.ts +17 -0
- package/dist/components/field/FieldCurrency.vue.js +7 -0
- package/dist/components/field/FieldCurrency.vue2.js +42 -0
- package/dist/components/field/FieldDate.vue.js +2 -2
- package/dist/components/field/FieldDate.vue2.js +13 -10
- package/dist/components/field/FieldDatetime.vue.js +1 -1
- package/dist/components/field/FieldMarkdownPreview.vue.d.ts +13 -0
- package/dist/components/field/FieldMarkdownPreview.vue.js +7 -0
- package/dist/components/field/FieldMarkdownPreview.vue2.js +37 -0
- package/dist/components/field/FieldMultiSelect.vue.js +2 -2
- package/dist/components/field/FieldPhone.vue.d.ts +17 -0
- package/dist/components/field/FieldPhone.vue.js +7 -0
- package/dist/components/field/FieldPhone.vue2.js +34 -0
- package/dist/components/field/FieldProgress.vue.d.ts +15 -0
- package/dist/components/field/FieldProgress.vue.js +7 -0
- package/dist/components/field/FieldProgress.vue2.js +40 -0
- package/dist/components/field/FieldRelation.vue.d.ts +17 -0
- package/dist/components/field/FieldRelation.vue.js +7 -0
- package/dist/components/field/FieldRelation.vue2.js +67 -0
- package/dist/components/field/FieldRichText.vue.d.ts +17 -0
- package/dist/components/field/FieldRichText.vue.js +7 -0
- package/dist/components/field/FieldRichText.vue2.js +65 -0
- package/dist/components/field/FieldSelect.vue.js +1 -1
- package/dist/components/field/FieldSelect.vue2.js +43 -42
- package/dist/components/form/FormDesigner.vue.js +2 -2
- package/dist/components/form/FormDesigner.vue2.js +62 -52
- package/dist/components/gallery/GalleryCard.vue.js +2 -2
- package/dist/components/gallery/GalleryView.vue.d.ts +6 -2
- package/dist/components/gallery/GalleryView.vue.js +2 -2
- package/dist/components/gallery/GalleryView.vue2.js +30 -20
- package/dist/components/kanban/KanbanBoard.vue.d.ts +5 -1
- package/dist/components/kanban/KanbanBoard.vue.js +4 -4
- package/dist/components/kanban/KanbanBoard.vue2.js +81 -48
- package/dist/components/layout/AppLayout.vue.js +2 -2
- package/dist/components/layout/AppLayout.vue2.js +46 -62
- package/dist/components/overlay/Drawer.vue.js +1 -1
- package/dist/components/overlay/Drawer.vue2.js +52 -68
- package/dist/components/overlay/Modal.vue.js +1 -1
- package/dist/components/overlay/Modal.vue2.js +52 -68
- package/dist/components/overlay/SidePanel.vue.js +2 -2
- package/dist/components/overlay/SidePanel.vue2.js +64 -80
- package/dist/components/table/ColumnHeaderMenu.vue.d.ts +33 -0
- package/dist/components/table/ColumnHeaderMenu.vue.js +7 -0
- package/dist/components/table/ColumnHeaderMenu.vue2.js +153 -0
- package/dist/components/table/DataTable.vue.d.ts +116 -25
- package/dist/components/table/DataTable.vue.js +4 -4
- package/dist/components/table/DataTable.vue2.js +775 -188
- package/dist/components/table/DetailSheet.vue.d.ts +43 -0
- package/dist/components/table/DetailSheet.vue.js +7 -0
- package/dist/components/table/DetailSheet.vue2.js +140 -0
- package/dist/components/table/FieldCell.vue.d.ts +1 -1
- package/dist/components/table/FieldCell.vue.js +1 -1
- package/dist/components/table/FieldCell.vue2.js +59 -44
- package/dist/components/table/FieldTypePicker.vue.d.ts +15 -0
- package/dist/components/table/FieldTypePicker.vue.js +7 -0
- package/dist/components/table/FieldTypePicker.vue2.js +92 -0
- package/dist/components/table/MobileListView.vue.d.ts +24 -0
- package/dist/components/table/MobileListView.vue.js +7 -0
- package/dist/components/table/MobileListView.vue2.js +90 -0
- package/dist/components/table/TableGroupRow.vue.d.ts +5 -0
- package/dist/components/table/TableGroupRow.vue.js +2 -2
- package/dist/components/table/TableGroupRow.vue2.js +33 -23
- package/dist/components/table/TableHeaderRow.vue.d.ts +16 -0
- package/dist/components/table/TableHeaderRow.vue.js +2 -2
- package/dist/components/table/TableHeaderRow.vue2.js +54 -33
- package/dist/components/table/TableToolbar.vue.d.ts +118 -0
- package/dist/components/table/TableToolbar.vue.js +7 -0
- package/dist/components/table/TableToolbar.vue2.js +273 -0
- package/dist/components/table/index.d.ts +5 -0
- package/dist/components/timeline/GanttTimeline.vue.js +1 -1
- package/dist/components/timeline/GanttTimeline.vue2.js +128 -127
- package/dist/components/toast/ToastItem.vue.js +3 -3
- package/dist/composables/index.d.ts +19 -0
- package/dist/composables/useBreakpoint.d.ts +2 -1
- package/dist/composables/useBreakpoint.js +14 -12
- package/dist/composables/useColumnResize.d.ts +19 -0
- package/dist/composables/useColumnResize.js +58 -0
- package/dist/composables/useDraftRows.d.ts +33 -0
- package/dist/composables/useDraftRows.js +103 -0
- package/dist/composables/useFixedColumns.d.ts +25 -0
- package/dist/composables/useFixedColumns.js +61 -0
- package/dist/composables/useFocusTrap.d.ts +10 -0
- package/dist/composables/useFocusTrap.js +37 -0
- package/dist/composables/useInlineEdit.js +3 -3
- package/dist/composables/useKeyboardNavigation.d.ts +45 -0
- package/dist/composables/useKeyboardNavigation.js +140 -0
- package/dist/composables/useRowDrag.d.ts +32 -0
- package/dist/composables/useRowDrag.js +85 -0
- package/dist/composables/useSchemaEngine.d.ts +31 -0
- package/dist/composables/useSchemaEngine.js +129 -0
- package/dist/composables/useSearch.d.ts +30 -0
- package/dist/composables/useSearch.js +59 -0
- package/dist/composables/useSupabaseProvider.d.ts +70 -0
- package/dist/composables/useSupabaseProvider.js +126 -0
- package/dist/composables/useTable.d.ts +3 -0
- package/dist/composables/useTable.js +103 -83
- package/dist/composables/useTableGroup.d.ts +14 -1
- package/dist/composables/useTableGroup.js +57 -33
- package/dist/composables/useViewPersistence.d.ts +98 -0
- package/dist/composables/useViewPersistence.js +141 -0
- package/dist/composables/useVirtualList.d.ts +4 -1
- package/dist/composables/useVirtualList.js +108 -85
- package/dist/composables/useWorkerSort.d.ts +14 -0
- package/dist/composables/useWorkerSort.js +61 -0
- package/dist/index.d.ts +28 -4
- package/dist/index.js +264 -216
- package/dist/style.css +1 -1
- package/dist/types/index.d.ts +63 -2
- package/dist/types/index.js +23 -6
- package/dist/types/table-internal.d.ts +64 -0
- package/dist/utils/aggregation.d.ts +5 -0
- package/dist/utils/aggregation.js +38 -0
- package/dist/utils/supabaseAdapter.d.ts +48 -0
- package/dist/utils/supabaseAdapter.js +76 -0
- package/dist/utils/supabaseSchema.d.ts +81 -0
- package/dist/utils/supabaseSchema.js +202 -0
- package/dist/workers/tableWorker.d.ts +31 -0
- package/package.json +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ref as f, computed as a, onUnmounted as w, readonly as d } from "vue";
|
|
2
|
+
function A(n) {
|
|
3
|
+
const m = n.selectorWidth ?? 40, i = f(0), u = f({ rowId: null });
|
|
4
|
+
let o = null, r = null, c = null;
|
|
5
|
+
const s = a(() => {
|
|
6
|
+
const e = new Set(n.fixedColumnKeys.value);
|
|
7
|
+
return n.columns.value.filter((l) => e.has(l.key));
|
|
8
|
+
}), v = a(() => {
|
|
9
|
+
const e = new Set(n.fixedColumnKeys.value);
|
|
10
|
+
return n.columns.value.filter((l) => !e.has(l.key));
|
|
11
|
+
}), h = a(() => {
|
|
12
|
+
let e = m;
|
|
13
|
+
for (const l of s.value)
|
|
14
|
+
e += typeof l.width == "number" ? l.width : l.minWidth ?? 200;
|
|
15
|
+
return e;
|
|
16
|
+
});
|
|
17
|
+
function x(e) {
|
|
18
|
+
if (c === "fixed") {
|
|
19
|
+
c = null;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const l = e.target;
|
|
23
|
+
i.value = l.scrollLeft, o !== null && cancelAnimationFrame(o), o = requestAnimationFrame(() => {
|
|
24
|
+
const t = n.fixedContainerRef.value;
|
|
25
|
+
t && t !== l && (c = "scroll", t.scrollTop = l.scrollTop), o = null;
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function y(e) {
|
|
29
|
+
if (c === "scroll") {
|
|
30
|
+
c = null;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const l = e.target;
|
|
34
|
+
r !== null && cancelAnimationFrame(r), r = requestAnimationFrame(() => {
|
|
35
|
+
const t = n.scrollContainerRef.value;
|
|
36
|
+
t && t !== l && (c = "fixed", t.scrollTop = l.scrollTop), r = null;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function F(e) {
|
|
40
|
+
u.value = { rowId: e };
|
|
41
|
+
}
|
|
42
|
+
function S(e) {
|
|
43
|
+
return u.value.rowId === e;
|
|
44
|
+
}
|
|
45
|
+
return w(() => {
|
|
46
|
+
o !== null && cancelAnimationFrame(o), r !== null && cancelAnimationFrame(r);
|
|
47
|
+
}), {
|
|
48
|
+
fixedColumns: s,
|
|
49
|
+
scrollableColumns: v,
|
|
50
|
+
fixedWidth: h,
|
|
51
|
+
handleScroll: x,
|
|
52
|
+
handleFixedScroll: y,
|
|
53
|
+
scrollLeft: d(i),
|
|
54
|
+
syncHover: F,
|
|
55
|
+
isRowHovered: S,
|
|
56
|
+
hoverState: d(u)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
A as useFixedColumns
|
|
61
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Focus trap composable — 管理 dialog/drawer/panel 的焦点陷阱
|
|
3
|
+
*
|
|
4
|
+
* 返回 containerRef(绑到容器元素),activate() 打开时调用,deactivate() 关闭时调用。
|
|
5
|
+
*/
|
|
6
|
+
export declare function useFocusTrap(): {
|
|
7
|
+
containerRef: import('vue').Ref<HTMLElement | null, HTMLElement | null>;
|
|
8
|
+
activate: () => void;
|
|
9
|
+
deactivate: () => void;
|
|
10
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ref as f, onBeforeUnmount as s, nextTick as d } from "vue";
|
|
2
|
+
function v() {
|
|
3
|
+
const o = f(null);
|
|
4
|
+
let t = null;
|
|
5
|
+
function r() {
|
|
6
|
+
return o.value ? Array.from(
|
|
7
|
+
o.value.querySelectorAll(
|
|
8
|
+
'button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"]), a[href]'
|
|
9
|
+
)
|
|
10
|
+
).filter((e) => !e.closest('[aria-hidden="true"]')) : [];
|
|
11
|
+
}
|
|
12
|
+
function u(e) {
|
|
13
|
+
if (e.key !== "Tab") return;
|
|
14
|
+
const n = r();
|
|
15
|
+
if (n.length === 0) {
|
|
16
|
+
e.preventDefault();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const c = n[0], i = n[n.length - 1];
|
|
20
|
+
e.shiftKey ? document.activeElement === c && (e.preventDefault(), i.focus()) : document.activeElement === i && (e.preventDefault(), c.focus());
|
|
21
|
+
}
|
|
22
|
+
function l() {
|
|
23
|
+
t = document.activeElement, d(() => {
|
|
24
|
+
const e = r();
|
|
25
|
+
e.length && e[0].focus(), document.addEventListener("keydown", u);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function a() {
|
|
29
|
+
document.removeEventListener("keydown", u), t == null || t.focus(), t = null;
|
|
30
|
+
}
|
|
31
|
+
return s(() => {
|
|
32
|
+
document.removeEventListener("keydown", u);
|
|
33
|
+
}), { containerRef: o, activate: l, deactivate: a };
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
v as useFocusTrap
|
|
37
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const n = f(null);
|
|
1
|
+
import { ref as c, readonly as f } from "vue";
|
|
3
2
|
function I() {
|
|
3
|
+
const n = c(null);
|
|
4
4
|
function o(e, l) {
|
|
5
5
|
n.value = { rowId: e, fieldId: l };
|
|
6
6
|
}
|
|
@@ -16,7 +16,7 @@ function I() {
|
|
|
16
16
|
return ((u = n.value) == null ? void 0 : u.rowId) === e && ((i = n.value) == null ? void 0 : i.fieldId) === l;
|
|
17
17
|
}
|
|
18
18
|
return {
|
|
19
|
-
editingCell:
|
|
19
|
+
editingCell: f(n),
|
|
20
20
|
activate: o,
|
|
21
21
|
commit: r,
|
|
22
22
|
cancel: a,
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
import { TableColumn } from '../types';
|
|
3
|
+
import { EditingCell } from './useInlineEdit';
|
|
4
|
+
export interface UseKeyboardNavigationOptions {
|
|
5
|
+
columns: Ref<TableColumn[]>;
|
|
6
|
+
rows: Ref<Array<{
|
|
7
|
+
id: string;
|
|
8
|
+
} & Record<string, any>>>;
|
|
9
|
+
containerRef: Ref<HTMLElement | null>;
|
|
10
|
+
editingCell: Ref<EditingCell | null>;
|
|
11
|
+
enabled: Ref<boolean>;
|
|
12
|
+
/** Callback to activate inline edit on a cell */
|
|
13
|
+
onActivateEdit?: (rowId: string, colKey: string) => void;
|
|
14
|
+
/** Callback to cancel inline edit */
|
|
15
|
+
onCancelEdit?: () => void;
|
|
16
|
+
/** Callback to scroll a row into view */
|
|
17
|
+
onScrollToRow?: (rowIndex: number) => void;
|
|
18
|
+
/** Callback to get cell text value for copy (optional, enables Ctrl+C) */
|
|
19
|
+
getCellValue?: (rowId: string, colKey: string) => string;
|
|
20
|
+
/** Callback when paste data is received (optional, enables Ctrl+V) */
|
|
21
|
+
onPaste?: (startRowId: string, startColKey: string, data: string[][]) => void;
|
|
22
|
+
}
|
|
23
|
+
export interface SelectedCell {
|
|
24
|
+
rowId: string;
|
|
25
|
+
colKey: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function useKeyboardNavigation(options: UseKeyboardNavigationOptions): {
|
|
28
|
+
activeCell: Readonly<Ref<{
|
|
29
|
+
readonly rowId: string;
|
|
30
|
+
readonly colKey: string;
|
|
31
|
+
} | null, {
|
|
32
|
+
readonly rowId: string;
|
|
33
|
+
readonly colKey: string;
|
|
34
|
+
} | null>>;
|
|
35
|
+
selectedRange: Readonly<Ref<readonly {
|
|
36
|
+
readonly rowId: string;
|
|
37
|
+
readonly colKey: string;
|
|
38
|
+
}[], readonly {
|
|
39
|
+
readonly rowId: string;
|
|
40
|
+
readonly colKey: string;
|
|
41
|
+
}[]>>;
|
|
42
|
+
setActiveCell: (rowId: string, colKey: string) => void;
|
|
43
|
+
clearActiveCell: () => void;
|
|
44
|
+
handleKeyDown: (e: KeyboardEvent) => void;
|
|
45
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { ref as g, readonly as A } from "vue";
|
|
2
|
+
function M(t) {
|
|
3
|
+
const l = g(null), i = g(null), s = g([]);
|
|
4
|
+
function k(e, a) {
|
|
5
|
+
l.value = { rowId: e, colKey: a }, i.value = null, s.value = [];
|
|
6
|
+
}
|
|
7
|
+
function m() {
|
|
8
|
+
l.value = null, i.value = null, s.value = [];
|
|
9
|
+
}
|
|
10
|
+
function h() {
|
|
11
|
+
return t.columns.value.filter((e) => !e.hidden);
|
|
12
|
+
}
|
|
13
|
+
function C(e, a) {
|
|
14
|
+
const n = t.rows.value, c = h(), r = n.findIndex((v) => v.id === e.rowId), u = c.findIndex((v) => v.key === e.colKey), o = n.findIndex((v) => v.id === a.rowId), y = c.findIndex((v) => v.key === a.colKey);
|
|
15
|
+
if (r === -1 || u === -1 || o === -1 || y === -1)
|
|
16
|
+
return [];
|
|
17
|
+
const I = Math.min(r, o), K = Math.max(r, o), d = Math.min(u, y), w = Math.max(u, y), f = [];
|
|
18
|
+
for (let v = I; v <= K; v++)
|
|
19
|
+
for (let x = d; x <= w; x++)
|
|
20
|
+
f.push({ rowId: n[v].id, colKey: c[x].key });
|
|
21
|
+
return f;
|
|
22
|
+
}
|
|
23
|
+
async function S() {
|
|
24
|
+
if (!t.getCellValue) return;
|
|
25
|
+
const e = s.value.length > 0 ? s.value : l.value ? [l.value] : [];
|
|
26
|
+
if (e.length === 0) return;
|
|
27
|
+
const a = t.rows.value, n = h(), c = [...new Set(e.map((d) => d.rowId))], r = [...new Set(e.map((d) => d.colKey))], u = c.sort((d, w) => a.findIndex((f) => f.id === d) - a.findIndex((f) => f.id === w)), o = r.sort((d, w) => n.findIndex((f) => f.key === d) - n.findIndex((f) => f.key === w)), y = new Set(e.map((d) => `${d.rowId}:${d.colKey}`)), I = [];
|
|
28
|
+
for (const d of u) {
|
|
29
|
+
const w = [];
|
|
30
|
+
for (const f of o)
|
|
31
|
+
y.has(`${d}:${f}`) ? w.push(t.getCellValue(d, f)) : w.push("");
|
|
32
|
+
I.push(w.join(" "));
|
|
33
|
+
}
|
|
34
|
+
const K = I.join(`
|
|
35
|
+
`);
|
|
36
|
+
try {
|
|
37
|
+
await navigator.clipboard.writeText(K);
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function E() {
|
|
42
|
+
if (!t.onPaste || !l.value) return;
|
|
43
|
+
let e;
|
|
44
|
+
try {
|
|
45
|
+
e = await navigator.clipboard.readText();
|
|
46
|
+
} catch {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (!e) return;
|
|
50
|
+
const a = e.split(/\r?\n/).filter((n) => n.length > 0).map((n) => n.split(" "));
|
|
51
|
+
a.length !== 0 && t.onPaste(l.value.rowId, l.value.colKey, a);
|
|
52
|
+
}
|
|
53
|
+
function T(e) {
|
|
54
|
+
var n, c, r;
|
|
55
|
+
if (!t.enabled.value) return;
|
|
56
|
+
if (t.editingCell.value !== null) {
|
|
57
|
+
if (e.key === "Escape") {
|
|
58
|
+
e.preventDefault(), (n = t.onCancelEdit) == null || n.call(t);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (e.key === "Tab") {
|
|
62
|
+
e.preventDefault(), (c = t.onCancelEdit) == null || c.call(t), R(e.shiftKey);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "c") {
|
|
68
|
+
e.preventDefault(), S();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "v") {
|
|
72
|
+
e.preventDefault(), E();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (!l.value) {
|
|
76
|
+
if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
const u = t.rows.value, o = h();
|
|
79
|
+
u.length > 0 && o.length > 0 && k(u[0].id, o[0].key);
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
switch (e.key) {
|
|
84
|
+
case "ArrowUp":
|
|
85
|
+
e.preventDefault(), D(-1, e.shiftKey);
|
|
86
|
+
break;
|
|
87
|
+
case "ArrowDown":
|
|
88
|
+
e.preventDefault(), D(1, e.shiftKey);
|
|
89
|
+
break;
|
|
90
|
+
case "ArrowLeft":
|
|
91
|
+
e.preventDefault(), b(-1, e.shiftKey);
|
|
92
|
+
break;
|
|
93
|
+
case "ArrowRight":
|
|
94
|
+
e.preventDefault(), b(1, e.shiftKey);
|
|
95
|
+
break;
|
|
96
|
+
case "Enter":
|
|
97
|
+
e.preventDefault(), l.value && ((r = t.onActivateEdit) == null || r.call(t, l.value.rowId, l.value.colKey));
|
|
98
|
+
break;
|
|
99
|
+
case "Escape":
|
|
100
|
+
e.preventDefault(), m();
|
|
101
|
+
break;
|
|
102
|
+
case "Tab":
|
|
103
|
+
e.preventDefault(), R(e.shiftKey);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function D(e, a = !1) {
|
|
108
|
+
var u;
|
|
109
|
+
if (!l.value) return;
|
|
110
|
+
const n = t.rows.value, c = n.findIndex((o) => o.id === l.value.rowId);
|
|
111
|
+
if (c === -1) return;
|
|
112
|
+
const r = c + e;
|
|
113
|
+
r < 0 || r >= n.length || (a ? (i.value || (i.value = { rowId: l.value.rowId, colKey: l.value.colKey }), l.value = { rowId: n[r].id, colKey: l.value.colKey }, s.value = C(i.value, l.value)) : (i.value = null, s.value = [], l.value = { rowId: n[r].id, colKey: l.value.colKey }), (u = t.onScrollToRow) == null || u.call(t, r));
|
|
114
|
+
}
|
|
115
|
+
function b(e, a = !1) {
|
|
116
|
+
if (!l.value) return;
|
|
117
|
+
const n = h(), c = n.findIndex((u) => u.key === l.value.colKey);
|
|
118
|
+
if (c === -1) return;
|
|
119
|
+
const r = c + e;
|
|
120
|
+
r < 0 || r >= n.length || (a ? (i.value || (i.value = { rowId: l.value.rowId, colKey: l.value.colKey }), l.value = { rowId: l.value.rowId, colKey: n[r].key }, s.value = C(i.value, l.value)) : (i.value = null, s.value = [], l.value = { rowId: l.value.rowId, colKey: n[r].key }));
|
|
121
|
+
}
|
|
122
|
+
function R(e) {
|
|
123
|
+
var y;
|
|
124
|
+
if (!l.value) return;
|
|
125
|
+
const a = t.rows.value, n = h(), c = a.findIndex((I) => I.id === l.value.rowId), r = n.findIndex((I) => I.key === l.value.colKey);
|
|
126
|
+
if (c === -1 || r === -1) return;
|
|
127
|
+
let u = r + (e ? -1 : 1), o = c;
|
|
128
|
+
u >= n.length ? (u = 0, o++) : u < 0 && (u = n.length - 1, o--), !(o < 0 || o >= a.length) && (l.value = { rowId: a[o].id, colKey: n[u].key }, i.value = null, s.value = [], (y = t.onScrollToRow) == null || y.call(t, o));
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
activeCell: A(l),
|
|
132
|
+
selectedRange: A(s),
|
|
133
|
+
setActiveCell: k,
|
|
134
|
+
clearActiveCell: m,
|
|
135
|
+
handleKeyDown: T
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export {
|
|
139
|
+
M as useKeyboardNavigation
|
|
140
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
import { GroupedListItem } from './useTableGroup';
|
|
3
|
+
export interface UseRowDragOptions<T> {
|
|
4
|
+
/** The flattened items list (with group headers) */
|
|
5
|
+
processedItems: Ref<GroupedListItem<T>[]>;
|
|
6
|
+
/** Whether cross-group drag is allowed */
|
|
7
|
+
enableCrossGroupDrag?: Ref<boolean>;
|
|
8
|
+
/** The field id used for grouping (needed for cross-group value update) */
|
|
9
|
+
groupFieldId?: Ref<string | undefined>;
|
|
10
|
+
}
|
|
11
|
+
export interface RowReorderPayload {
|
|
12
|
+
rowId: string;
|
|
13
|
+
beforeRowId: string | null;
|
|
14
|
+
}
|
|
15
|
+
export interface RowGroupChangePayload {
|
|
16
|
+
rowId: string;
|
|
17
|
+
groupFieldId: string;
|
|
18
|
+
newValue: unknown;
|
|
19
|
+
}
|
|
20
|
+
export declare function useRowDrag<T extends Record<string, any>>(options: UseRowDragOptions<T>): {
|
|
21
|
+
isDragging: Readonly<Ref<boolean, boolean>>;
|
|
22
|
+
draggedRowId: Readonly<Ref<string | null, string | null>>;
|
|
23
|
+
dropTargetId: Readonly<Ref<string | null, string | null>>;
|
|
24
|
+
dropPosition: Readonly<Ref<"before" | "after" | "into-group", "before" | "after" | "into-group">>;
|
|
25
|
+
handleDragStart: (e: DragEvent, item: GroupedListItem<T>) => void;
|
|
26
|
+
handleDragOver: (e: DragEvent, targetItem: GroupedListItem<T>) => void;
|
|
27
|
+
handleDrop: (e: DragEvent, targetItem: GroupedListItem<T>, callbacks: {
|
|
28
|
+
onReorder?: (payload: RowReorderPayload) => void;
|
|
29
|
+
onGroupChange?: (payload: RowGroupChangePayload) => void;
|
|
30
|
+
}) => void;
|
|
31
|
+
handleDragEnd: () => void;
|
|
32
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ref as c, readonly as D } from "vue";
|
|
2
|
+
function y(i) {
|
|
3
|
+
const I = c(!1), u = c(null), g = c(null), s = c("before");
|
|
4
|
+
let v = null;
|
|
5
|
+
function f(e) {
|
|
6
|
+
return (e == null ? void 0 : e.__type) === "group-header";
|
|
7
|
+
}
|
|
8
|
+
function d(e) {
|
|
9
|
+
return String(e.id ?? "");
|
|
10
|
+
}
|
|
11
|
+
function T(e, r) {
|
|
12
|
+
if (f(r)) {
|
|
13
|
+
e.preventDefault();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
v = r, u.value = d(r), I.value = !0, e.dataTransfer && (e.dataTransfer.effectAllowed = "move", e.dataTransfer.setData("text/plain", u.value));
|
|
17
|
+
}
|
|
18
|
+
function R(e, r) {
|
|
19
|
+
var l;
|
|
20
|
+
if (e.preventDefault(), !v) return;
|
|
21
|
+
if (e.dataTransfer && (e.dataTransfer.dropEffect = "move"), f(r)) {
|
|
22
|
+
(l = i.enableCrossGroupDrag) != null && l.value && (g.value = r.id, s.value = "into-group");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const n = e.currentTarget;
|
|
26
|
+
if (!n) return;
|
|
27
|
+
const t = n.getBoundingClientRect(), p = e.clientY - t.top < t.height / 2;
|
|
28
|
+
g.value = d(r), s.value = p ? "before" : "after";
|
|
29
|
+
}
|
|
30
|
+
function C(e, r, n) {
|
|
31
|
+
var o, p, l, w;
|
|
32
|
+
if (e.preventDefault(), !v || !u.value) {
|
|
33
|
+
h();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const t = u.value;
|
|
37
|
+
if (f(r)) {
|
|
38
|
+
const a = (o = i.groupFieldId) == null ? void 0 : o.value;
|
|
39
|
+
a && ((p = i.enableCrossGroupDrag) != null && p.value) && ((l = n.onGroupChange) == null || l.call(n, {
|
|
40
|
+
rowId: t,
|
|
41
|
+
groupFieldId: a,
|
|
42
|
+
newValue: r.__groupKey
|
|
43
|
+
}));
|
|
44
|
+
} else {
|
|
45
|
+
const a = d(r);
|
|
46
|
+
t !== a && ((w = n.onReorder) == null || w.call(n, {
|
|
47
|
+
rowId: t,
|
|
48
|
+
beforeRowId: s.value === "before" ? a : _(a)
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
h();
|
|
52
|
+
}
|
|
53
|
+
function G() {
|
|
54
|
+
h();
|
|
55
|
+
}
|
|
56
|
+
function _(e) {
|
|
57
|
+
const r = i.processedItems.value;
|
|
58
|
+
for (let n = 0; n < r.length; n++) {
|
|
59
|
+
const t = r[n];
|
|
60
|
+
if (!f(t) && d(t) === e) {
|
|
61
|
+
for (let o = n + 1; o < r.length; o++)
|
|
62
|
+
if (!f(r[o]))
|
|
63
|
+
return d(r[o]);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
function h() {
|
|
70
|
+
I.value = !1, u.value = null, g.value = null, v = null;
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
isDragging: D(I),
|
|
74
|
+
draggedRowId: D(u),
|
|
75
|
+
dropTargetId: D(g),
|
|
76
|
+
dropPosition: D(s),
|
|
77
|
+
handleDragStart: T,
|
|
78
|
+
handleDragOver: R,
|
|
79
|
+
handleDrop: C,
|
|
80
|
+
handleDragEnd: G
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export {
|
|
84
|
+
y as useRowDrag
|
|
85
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Ref, ComputedRef } from 'vue';
|
|
2
|
+
import { FieldDef, TableColumn, TableSchema, ViewConfig } from '../types';
|
|
3
|
+
export interface ValidationRule {
|
|
4
|
+
fieldId: string;
|
|
5
|
+
type: "required" | "min" | "max" | "pattern" | "custom";
|
|
6
|
+
message: string;
|
|
7
|
+
validate: (value: unknown) => boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface FilterFieldConfig {
|
|
10
|
+
fieldId: string;
|
|
11
|
+
label: string;
|
|
12
|
+
type: "string" | "number" | "date" | "select" | "boolean";
|
|
13
|
+
options?: {
|
|
14
|
+
value: string;
|
|
15
|
+
label: string;
|
|
16
|
+
}[];
|
|
17
|
+
}
|
|
18
|
+
export interface FilterPanelConfig {
|
|
19
|
+
fields: FilterFieldConfig[];
|
|
20
|
+
}
|
|
21
|
+
export interface UseSchemaEngineOptions {
|
|
22
|
+
schema: Ref<TableSchema | undefined>;
|
|
23
|
+
view?: Ref<ViewConfig | undefined>;
|
|
24
|
+
}
|
|
25
|
+
export declare function useSchemaEngine(options: UseSchemaEngineOptions): {
|
|
26
|
+
columns: ComputedRef<TableColumn[]>;
|
|
27
|
+
fieldDefs: ComputedRef<FieldDef[]>;
|
|
28
|
+
validationRules: ComputedRef<Map<string, ValidationRule[]>>;
|
|
29
|
+
filterConfig: ComputedRef<FilterPanelConfig>;
|
|
30
|
+
formulaDependencyGraph: ComputedRef<Map<string, string[]>>;
|
|
31
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { computed as l } from "vue";
|
|
2
|
+
function b(n) {
|
|
3
|
+
const r = l(() => {
|
|
4
|
+
var e;
|
|
5
|
+
const s = n.schema.value;
|
|
6
|
+
if (!s) return [];
|
|
7
|
+
const i = (e = n.view) == null ? void 0 : e.value;
|
|
8
|
+
if (i != null && i.visibleFields && i.visibleFields.length > 0) {
|
|
9
|
+
const a = new Map(s.fields.map((t) => [t.id, t]));
|
|
10
|
+
return i.visibleFields.map((t) => a.get(t)).filter((t) => t !== void 0 && !t.hidden);
|
|
11
|
+
}
|
|
12
|
+
return s.fields.filter((a) => !a.hidden);
|
|
13
|
+
}), c = l(() => r.value.map((s) => f(s))), m = l(() => {
|
|
14
|
+
const s = /* @__PURE__ */ new Map(), i = n.schema.value;
|
|
15
|
+
if (!i) return s;
|
|
16
|
+
for (const e of i.fields) {
|
|
17
|
+
const a = [];
|
|
18
|
+
if (e.required && a.push({
|
|
19
|
+
fieldId: e.id,
|
|
20
|
+
type: "required",
|
|
21
|
+
message: `${e.name} 为必填项`,
|
|
22
|
+
validate: (t) => t != null && t !== ""
|
|
23
|
+
}), e.type === "number") {
|
|
24
|
+
if (e.min !== void 0) {
|
|
25
|
+
const t = e.min;
|
|
26
|
+
a.push({
|
|
27
|
+
fieldId: e.id,
|
|
28
|
+
type: "min",
|
|
29
|
+
message: `${e.name} 不能小于 ${t}`,
|
|
30
|
+
validate: (d) => typeof d == "number" && d >= t
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (e.max !== void 0) {
|
|
34
|
+
const t = e.max;
|
|
35
|
+
a.push({
|
|
36
|
+
fieldId: e.id,
|
|
37
|
+
type: "max",
|
|
38
|
+
message: `${e.name} 不能大于 ${t}`,
|
|
39
|
+
validate: (d) => typeof d == "number" && d <= t
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
e.type === "email" && a.push({
|
|
44
|
+
fieldId: e.id,
|
|
45
|
+
type: "pattern",
|
|
46
|
+
message: `${e.name} 格式不正确`,
|
|
47
|
+
validate: (t) => typeof t != "string" || t === "" || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)
|
|
48
|
+
}), e.type === "url" && a.push({
|
|
49
|
+
fieldId: e.id,
|
|
50
|
+
type: "pattern",
|
|
51
|
+
message: `${e.name} 不是有效的 URL`,
|
|
52
|
+
validate: (t) => {
|
|
53
|
+
if (typeof t != "string" || t === "") return !0;
|
|
54
|
+
try {
|
|
55
|
+
return new URL(t), !0;
|
|
56
|
+
} catch {
|
|
57
|
+
return !1;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}), a.length > 0 && s.set(e.id, a);
|
|
61
|
+
}
|
|
62
|
+
return s;
|
|
63
|
+
}), u = l(() => ({
|
|
64
|
+
fields: r.value.map((s) => y(s))
|
|
65
|
+
})), o = l(() => {
|
|
66
|
+
const s = /* @__PURE__ */ new Map(), i = n.schema.value;
|
|
67
|
+
if (!i) return s;
|
|
68
|
+
for (const e of i.fields)
|
|
69
|
+
e.type === "formula" && e.dependencies && s.set(e.id, [...e.dependencies]);
|
|
70
|
+
return s;
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
columns: c,
|
|
74
|
+
fieldDefs: r,
|
|
75
|
+
validationRules: m,
|
|
76
|
+
filterConfig: u,
|
|
77
|
+
formulaDependencyGraph: o
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const p = {
|
|
81
|
+
text: "string",
|
|
82
|
+
number: "number",
|
|
83
|
+
currency: "number",
|
|
84
|
+
progress: "number",
|
|
85
|
+
rating: "number",
|
|
86
|
+
date: "date",
|
|
87
|
+
datetime: "date",
|
|
88
|
+
select: "status",
|
|
89
|
+
multi_select: "status"
|
|
90
|
+
};
|
|
91
|
+
function f(n) {
|
|
92
|
+
return {
|
|
93
|
+
key: n.id,
|
|
94
|
+
label: n.name,
|
|
95
|
+
width: n.width ?? "fill",
|
|
96
|
+
type: p[n.type] ?? "string",
|
|
97
|
+
hidden: n.hidden
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function y(n) {
|
|
101
|
+
const r = {
|
|
102
|
+
fieldId: n.id,
|
|
103
|
+
label: n.name,
|
|
104
|
+
type: "string"
|
|
105
|
+
};
|
|
106
|
+
switch (n.type) {
|
|
107
|
+
case "number":
|
|
108
|
+
case "currency":
|
|
109
|
+
case "progress":
|
|
110
|
+
case "rating":
|
|
111
|
+
r.type = "number";
|
|
112
|
+
break;
|
|
113
|
+
case "date":
|
|
114
|
+
case "datetime":
|
|
115
|
+
r.type = "date";
|
|
116
|
+
break;
|
|
117
|
+
case "checkbox":
|
|
118
|
+
r.type = "boolean";
|
|
119
|
+
break;
|
|
120
|
+
case "select":
|
|
121
|
+
case "multi_select":
|
|
122
|
+
r.type = "select", r.options = n.options.map((c) => ({ value: c.value, label: c.label }));
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
return r;
|
|
126
|
+
}
|
|
127
|
+
export {
|
|
128
|
+
b as useSchemaEngine
|
|
129
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface UseSearchOptions {
|
|
2
|
+
/** 防抖延迟(ms),默认 300 */
|
|
3
|
+
debounceMs?: number;
|
|
4
|
+
/** 最小搜索字符数,默认 1 */
|
|
5
|
+
minChars?: number;
|
|
6
|
+
/** 搜索模式 */
|
|
7
|
+
mode?: "client" | "server";
|
|
8
|
+
}
|
|
9
|
+
export interface SearchHighlight {
|
|
10
|
+
/** 原始文本 */
|
|
11
|
+
text: string;
|
|
12
|
+
/** 是否为匹配片段 */
|
|
13
|
+
match: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function useSearch(options?: UseSearchOptions): {
|
|
16
|
+
/** 绑定到 input 的 v-model */
|
|
17
|
+
inputKeyword: import('vue').Ref<string, string>;
|
|
18
|
+
/** 防抖后的搜索词(传给 useSupabaseProvider 的 searchKeyword) */
|
|
19
|
+
keyword: Readonly<import('vue').Ref<string, string>>;
|
|
20
|
+
/** 是否有有效搜索词 */
|
|
21
|
+
isSearching: import('vue').ComputedRef<boolean>;
|
|
22
|
+
/** 高亮文本分段 */
|
|
23
|
+
highlightText: (text: string, query?: string) => SearchHighlight[];
|
|
24
|
+
/** 客户端过滤 */
|
|
25
|
+
filterBySearch: <T extends Record<string, unknown>>(data: T[], searchColumns: string[]) => T[];
|
|
26
|
+
/** 清空 */
|
|
27
|
+
clearSearch: () => void;
|
|
28
|
+
/** 直接设置 */
|
|
29
|
+
setKeyword: (val: string) => void;
|
|
30
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ref as f, computed as y, watch as T, readonly as S } from "vue";
|
|
2
|
+
function L(m = {}) {
|
|
3
|
+
const { debounceMs: h = 300, minChars: a = 1 } = m, u = f(""), r = f(""), w = y(() => r.value.length >= a);
|
|
4
|
+
let t = null;
|
|
5
|
+
T(u, (e) => {
|
|
6
|
+
t && clearTimeout(t);
|
|
7
|
+
const n = e.trim();
|
|
8
|
+
if (n.length < a) {
|
|
9
|
+
r.value = "";
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
t = setTimeout(() => {
|
|
13
|
+
r.value = n;
|
|
14
|
+
}, h);
|
|
15
|
+
});
|
|
16
|
+
function d(e, n) {
|
|
17
|
+
const o = n ?? r.value;
|
|
18
|
+
if (!o || !e)
|
|
19
|
+
return [{ text: e, match: !1 }];
|
|
20
|
+
const i = o.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), c = new RegExp(`(${i})`, "gi");
|
|
21
|
+
return e.split(c).filter(Boolean).map((s) => ({
|
|
22
|
+
text: s,
|
|
23
|
+
match: s.toLowerCase() === o.toLowerCase()
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
function g(e, n) {
|
|
27
|
+
if (!r.value || !n.length) return e;
|
|
28
|
+
const o = r.value.toLowerCase();
|
|
29
|
+
return e.filter((i) => n.some((c) => {
|
|
30
|
+
const l = i[c];
|
|
31
|
+
return l == null ? !1 : String(l).toLowerCase().includes(o);
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
function p() {
|
|
35
|
+
u.value = "", r.value = "", t && clearTimeout(t);
|
|
36
|
+
}
|
|
37
|
+
function v(e) {
|
|
38
|
+
u.value = e, r.value = e.trim(), t && clearTimeout(t);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
/** 绑定到 input 的 v-model */
|
|
42
|
+
inputKeyword: u,
|
|
43
|
+
/** 防抖后的搜索词(传给 useSupabaseProvider 的 searchKeyword) */
|
|
44
|
+
keyword: S(r),
|
|
45
|
+
/** 是否有有效搜索词 */
|
|
46
|
+
isSearching: w,
|
|
47
|
+
/** 高亮文本分段 */
|
|
48
|
+
highlightText: d,
|
|
49
|
+
/** 客户端过滤 */
|
|
50
|
+
filterBySearch: g,
|
|
51
|
+
/** 清空 */
|
|
52
|
+
clearSearch: p,
|
|
53
|
+
/** 直接设置 */
|
|
54
|
+
setKeyword: v
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
L as useSearch
|
|
59
|
+
};
|