@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.
Files changed (149) hide show
  1. package/dist/assets/tableWorker-CTsbCPPP.js +1 -0
  2. package/dist/components/ContextMenu/index.vue.d.ts +2 -1
  3. package/dist/components/ContextMenu/index.vue.js +2 -2
  4. package/dist/components/ContextMenu/index.vue2.js +37 -36
  5. package/dist/components/base/DropdownMenu.vue.js +2 -2
  6. package/dist/components/base/DropdownMenu.vue2.js +38 -39
  7. package/dist/components/base/MonitorItem.vue.d.ts +1 -1
  8. package/dist/components/base/PersonaCard.vue.d.ts +2 -1
  9. package/dist/components/base/PersonaCard.vue.js +3 -3
  10. package/dist/components/base/PersonaCard.vue2.js +51 -39
  11. package/dist/components/base/RefTag.vue.d.ts +2 -2
  12. package/dist/components/base/RefTag.vue.js +3 -3
  13. package/dist/components/base/SearchHighlight.vue.d.ts +6 -0
  14. package/dist/components/base/SearchHighlight.vue.js +7 -0
  15. package/dist/components/base/SearchHighlight.vue2.js +21 -0
  16. package/dist/components/base/ToolbarBtn.vue.d.ts +2 -1
  17. package/dist/components/base/ToolbarBtn.vue.js +1 -1
  18. package/dist/components/base/ViewModeGroup.vue.d.ts +2 -1
  19. package/dist/components/base/ViewModeGroup.vue.js +3 -3
  20. package/dist/components/base/ViewSwitcher.vue.d.ts +2 -1
  21. package/dist/components/base/ViewSwitcher.vue.js +2 -2
  22. package/dist/components/base/index.d.ts +1 -0
  23. package/dist/components/field/FieldAttachment.vue.d.ts +17 -0
  24. package/dist/components/field/FieldAttachment.vue.js +7 -0
  25. package/dist/components/field/FieldAttachment.vue2.js +69 -0
  26. package/dist/components/field/FieldAutoNumber.vue.d.ts +7 -0
  27. package/dist/components/field/FieldAutoNumber.vue.js +7 -0
  28. package/dist/components/field/FieldAutoNumber.vue2.js +15 -0
  29. package/dist/components/field/FieldCreator.vue.d.ts +7 -0
  30. package/dist/components/field/FieldCreator.vue.js +7 -0
  31. package/dist/components/field/FieldCreator.vue2.js +24 -0
  32. package/dist/components/field/FieldCurrency.vue.d.ts +17 -0
  33. package/dist/components/field/FieldCurrency.vue.js +7 -0
  34. package/dist/components/field/FieldCurrency.vue2.js +42 -0
  35. package/dist/components/field/FieldDate.vue.js +2 -2
  36. package/dist/components/field/FieldDate.vue2.js +13 -10
  37. package/dist/components/field/FieldDatetime.vue.js +1 -1
  38. package/dist/components/field/FieldMarkdownPreview.vue.d.ts +13 -0
  39. package/dist/components/field/FieldMarkdownPreview.vue.js +7 -0
  40. package/dist/components/field/FieldMarkdownPreview.vue2.js +37 -0
  41. package/dist/components/field/FieldMultiSelect.vue.js +2 -2
  42. package/dist/components/field/FieldPhone.vue.d.ts +17 -0
  43. package/dist/components/field/FieldPhone.vue.js +7 -0
  44. package/dist/components/field/FieldPhone.vue2.js +34 -0
  45. package/dist/components/field/FieldProgress.vue.d.ts +15 -0
  46. package/dist/components/field/FieldProgress.vue.js +7 -0
  47. package/dist/components/field/FieldProgress.vue2.js +40 -0
  48. package/dist/components/field/FieldRelation.vue.d.ts +17 -0
  49. package/dist/components/field/FieldRelation.vue.js +7 -0
  50. package/dist/components/field/FieldRelation.vue2.js +67 -0
  51. package/dist/components/field/FieldRichText.vue.d.ts +17 -0
  52. package/dist/components/field/FieldRichText.vue.js +7 -0
  53. package/dist/components/field/FieldRichText.vue2.js +65 -0
  54. package/dist/components/field/FieldSelect.vue.js +1 -1
  55. package/dist/components/field/FieldSelect.vue2.js +43 -42
  56. package/dist/components/form/FormDesigner.vue.js +2 -2
  57. package/dist/components/form/FormDesigner.vue2.js +62 -52
  58. package/dist/components/gallery/GalleryCard.vue.js +2 -2
  59. package/dist/components/gallery/GalleryView.vue.d.ts +6 -2
  60. package/dist/components/gallery/GalleryView.vue.js +2 -2
  61. package/dist/components/gallery/GalleryView.vue2.js +30 -20
  62. package/dist/components/kanban/KanbanBoard.vue.d.ts +5 -1
  63. package/dist/components/kanban/KanbanBoard.vue.js +4 -4
  64. package/dist/components/kanban/KanbanBoard.vue2.js +81 -48
  65. package/dist/components/layout/AppLayout.vue.js +2 -2
  66. package/dist/components/layout/AppLayout.vue2.js +46 -62
  67. package/dist/components/overlay/Drawer.vue.js +1 -1
  68. package/dist/components/overlay/Drawer.vue2.js +52 -68
  69. package/dist/components/overlay/Modal.vue.js +1 -1
  70. package/dist/components/overlay/Modal.vue2.js +52 -68
  71. package/dist/components/overlay/SidePanel.vue.js +2 -2
  72. package/dist/components/overlay/SidePanel.vue2.js +64 -80
  73. package/dist/components/table/ColumnHeaderMenu.vue.d.ts +33 -0
  74. package/dist/components/table/ColumnHeaderMenu.vue.js +7 -0
  75. package/dist/components/table/ColumnHeaderMenu.vue2.js +153 -0
  76. package/dist/components/table/DataTable.vue.d.ts +116 -25
  77. package/dist/components/table/DataTable.vue.js +4 -4
  78. package/dist/components/table/DataTable.vue2.js +775 -188
  79. package/dist/components/table/DetailSheet.vue.d.ts +43 -0
  80. package/dist/components/table/DetailSheet.vue.js +7 -0
  81. package/dist/components/table/DetailSheet.vue2.js +140 -0
  82. package/dist/components/table/FieldCell.vue.d.ts +1 -1
  83. package/dist/components/table/FieldCell.vue.js +1 -1
  84. package/dist/components/table/FieldCell.vue2.js +59 -44
  85. package/dist/components/table/FieldTypePicker.vue.d.ts +15 -0
  86. package/dist/components/table/FieldTypePicker.vue.js +7 -0
  87. package/dist/components/table/FieldTypePicker.vue2.js +92 -0
  88. package/dist/components/table/MobileListView.vue.d.ts +24 -0
  89. package/dist/components/table/MobileListView.vue.js +7 -0
  90. package/dist/components/table/MobileListView.vue2.js +90 -0
  91. package/dist/components/table/TableGroupRow.vue.d.ts +5 -0
  92. package/dist/components/table/TableGroupRow.vue.js +2 -2
  93. package/dist/components/table/TableGroupRow.vue2.js +33 -23
  94. package/dist/components/table/TableHeaderRow.vue.d.ts +16 -0
  95. package/dist/components/table/TableHeaderRow.vue.js +2 -2
  96. package/dist/components/table/TableHeaderRow.vue2.js +54 -33
  97. package/dist/components/table/TableToolbar.vue.d.ts +118 -0
  98. package/dist/components/table/TableToolbar.vue.js +7 -0
  99. package/dist/components/table/TableToolbar.vue2.js +273 -0
  100. package/dist/components/table/index.d.ts +5 -0
  101. package/dist/components/timeline/GanttTimeline.vue.js +1 -1
  102. package/dist/components/timeline/GanttTimeline.vue2.js +128 -127
  103. package/dist/components/toast/ToastItem.vue.js +3 -3
  104. package/dist/composables/index.d.ts +19 -0
  105. package/dist/composables/useBreakpoint.d.ts +2 -1
  106. package/dist/composables/useBreakpoint.js +14 -12
  107. package/dist/composables/useColumnResize.d.ts +19 -0
  108. package/dist/composables/useColumnResize.js +58 -0
  109. package/dist/composables/useDraftRows.d.ts +33 -0
  110. package/dist/composables/useDraftRows.js +103 -0
  111. package/dist/composables/useFixedColumns.d.ts +25 -0
  112. package/dist/composables/useFixedColumns.js +61 -0
  113. package/dist/composables/useFocusTrap.d.ts +10 -0
  114. package/dist/composables/useFocusTrap.js +37 -0
  115. package/dist/composables/useInlineEdit.js +3 -3
  116. package/dist/composables/useKeyboardNavigation.d.ts +45 -0
  117. package/dist/composables/useKeyboardNavigation.js +140 -0
  118. package/dist/composables/useRowDrag.d.ts +32 -0
  119. package/dist/composables/useRowDrag.js +85 -0
  120. package/dist/composables/useSchemaEngine.d.ts +31 -0
  121. package/dist/composables/useSchemaEngine.js +129 -0
  122. package/dist/composables/useSearch.d.ts +30 -0
  123. package/dist/composables/useSearch.js +59 -0
  124. package/dist/composables/useSupabaseProvider.d.ts +70 -0
  125. package/dist/composables/useSupabaseProvider.js +126 -0
  126. package/dist/composables/useTable.d.ts +3 -0
  127. package/dist/composables/useTable.js +103 -83
  128. package/dist/composables/useTableGroup.d.ts +14 -1
  129. package/dist/composables/useTableGroup.js +57 -33
  130. package/dist/composables/useViewPersistence.d.ts +98 -0
  131. package/dist/composables/useViewPersistence.js +141 -0
  132. package/dist/composables/useVirtualList.d.ts +4 -1
  133. package/dist/composables/useVirtualList.js +108 -85
  134. package/dist/composables/useWorkerSort.d.ts +14 -0
  135. package/dist/composables/useWorkerSort.js +61 -0
  136. package/dist/index.d.ts +28 -4
  137. package/dist/index.js +264 -216
  138. package/dist/style.css +1 -1
  139. package/dist/types/index.d.ts +63 -2
  140. package/dist/types/index.js +23 -6
  141. package/dist/types/table-internal.d.ts +64 -0
  142. package/dist/utils/aggregation.d.ts +5 -0
  143. package/dist/utils/aggregation.js +38 -0
  144. package/dist/utils/supabaseAdapter.d.ts +48 -0
  145. package/dist/utils/supabaseAdapter.js +76 -0
  146. package/dist/utils/supabaseSchema.d.ts +81 -0
  147. package/dist/utils/supabaseSchema.js +202 -0
  148. package/dist/workers/tableWorker.d.ts +31 -0
  149. package/package.json +1 -1
@@ -0,0 +1,70 @@
1
+ import { Ref } from 'vue';
2
+ import { SortState } from './useTable';
3
+ import { FilterCondition as TableFilterCondition, FilterLogic } from './useTableFilter';
4
+ export interface SupabaseQueryBuilder {
5
+ from(table: string): SupabaseFilterBuilder;
6
+ }
7
+ export interface SupabaseFilterBuilder {
8
+ select(columns?: string, options?: {
9
+ count?: "exact" | "planned" | "estimated";
10
+ }): SupabaseFilterBuilder;
11
+ order(column: string, options?: {
12
+ ascending?: boolean;
13
+ }): SupabaseFilterBuilder;
14
+ range(from: number, to: number): SupabaseFilterBuilder;
15
+ eq(column: string, value: unknown): SupabaseFilterBuilder;
16
+ neq(column: string, value: unknown): SupabaseFilterBuilder;
17
+ gt(column: string, value: unknown): SupabaseFilterBuilder;
18
+ gte(column: string, value: unknown): SupabaseFilterBuilder;
19
+ lt(column: string, value: unknown): SupabaseFilterBuilder;
20
+ lte(column: string, value: unknown): SupabaseFilterBuilder;
21
+ like(column: string, pattern: string): SupabaseFilterBuilder;
22
+ ilike(column: string, pattern: string): SupabaseFilterBuilder;
23
+ is(column: string, value: null): SupabaseFilterBuilder;
24
+ not(column: string, operator: string, value: unknown): SupabaseFilterBuilder;
25
+ in(column: string, values: unknown[]): SupabaseFilterBuilder;
26
+ textSearch(column: string, query: string, options?: {
27
+ type?: "plain" | "phrase" | "websearch";
28
+ }): SupabaseFilterBuilder;
29
+ or(filters: string): SupabaseFilterBuilder;
30
+ then<T>(onfulfilled?: (value: any) => T): Promise<T>;
31
+ }
32
+ export interface UseSupabaseProviderOptions {
33
+ /** Supabase client instance (pass your createClient() result) */
34
+ client: SupabaseQueryBuilder;
35
+ /** Supabase table name */
36
+ table: string;
37
+ /** select columns, defaults to '*' */
38
+ select?: string;
39
+ /** Fixed filter conditions always appended (e.g. { project: 'xxx' }) */
40
+ baseFilters?: Record<string, unknown>;
41
+ /** Reactive filter conditions from useTableFilter */
42
+ filterConditions?: Ref<readonly TableFilterCondition[]>;
43
+ /** Filter logic: and / or */
44
+ filterLogic?: Ref<FilterLogic>;
45
+ /** Full-text search keyword */
46
+ searchKeyword?: Ref<string>;
47
+ /** Columns to search with ilike */
48
+ searchColumns?: string[];
49
+ /** Row transformer: Supabase row -> your T type */
50
+ transform?: (row: Record<string, unknown>) => Record<string, unknown>;
51
+ }
52
+ export declare function useSupabaseProvider<T extends Record<string, unknown> = Record<string, unknown>>(options: UseSupabaseProviderOptions): {
53
+ onFetch: (params: {
54
+ page: number;
55
+ pageSize: number;
56
+ sort: SortState;
57
+ }) => Promise<{
58
+ data: T[];
59
+ total: number;
60
+ }>;
61
+ totalCount: Readonly<Ref<number, number>>;
62
+ loading: Readonly<Ref<boolean, boolean>>;
63
+ error: Readonly<Ref<Error | null, Error | null>>;
64
+ /** Exposed for advanced usage */
65
+ buildQuery: (params: {
66
+ page: number;
67
+ pageSize: number;
68
+ sort: SortState;
69
+ }) => SupabaseFilterBuilder;
70
+ };
@@ -0,0 +1,126 @@
1
+ import { ref as d, readonly as $ } from "vue";
2
+ function w(e, r) {
3
+ const { field: n, operator: t, value: l } = r;
4
+ switch (t) {
5
+ case "equals":
6
+ return e.eq(n, l);
7
+ case "not_equals":
8
+ return e.neq(n, l);
9
+ case "contains":
10
+ return e.ilike(n, `%${l}%`);
11
+ case "not_contains":
12
+ return e.not(n, "ilike", `%${l}%`);
13
+ case "starts_with":
14
+ return e.ilike(n, `${l}%`);
15
+ case "ends_with":
16
+ return e.ilike(n, `%${l}`);
17
+ case "gt":
18
+ return e.gt(n, l);
19
+ case "gte":
20
+ return e.gte(n, l);
21
+ case "lt":
22
+ return e.lt(n, l);
23
+ case "lte":
24
+ return e.lte(n, l);
25
+ case "is_empty":
26
+ return e.is(n, null);
27
+ case "is_not_empty":
28
+ return e.not(n, "is", null);
29
+ default:
30
+ return e;
31
+ }
32
+ }
33
+ function k(e) {
34
+ const { field: r, operator: n, value: t } = e;
35
+ switch (n) {
36
+ case "equals":
37
+ return `${r}.eq.${t}`;
38
+ case "not_equals":
39
+ return `${r}.neq.${t}`;
40
+ case "contains":
41
+ return `${r}.ilike.%${t}%`;
42
+ case "not_contains":
43
+ return `${r}.not.ilike.%${t}%`;
44
+ case "starts_with":
45
+ return `${r}.ilike.${t}%`;
46
+ case "ends_with":
47
+ return `${r}.ilike.%${t}`;
48
+ case "gt":
49
+ return `${r}.gt.${t}`;
50
+ case "gte":
51
+ return `${r}.gte.${t}`;
52
+ case "lt":
53
+ return `${r}.lt.${t}`;
54
+ case "lte":
55
+ return `${r}.lte.${t}`;
56
+ case "is_empty":
57
+ return `${r}.is.null`;
58
+ case "is_not_empty":
59
+ return `${r}.not.is.null`;
60
+ default:
61
+ return null;
62
+ }
63
+ }
64
+ function C(e, r, n) {
65
+ if (r.length === 0) return e;
66
+ if (n === "or") {
67
+ const t = r.map((l) => k(l)).filter(Boolean);
68
+ return t.length > 0 && (e = e.or(t.join(","))), e;
69
+ }
70
+ for (const t of r)
71
+ !t.field || !t.value && t.operator !== "is_empty" && t.operator !== "is_not_empty" || (e = w(e, t));
72
+ return e;
73
+ }
74
+ function p(e) {
75
+ const r = d(0), n = d(!1), t = d(null);
76
+ function l(o) {
77
+ var i, g, h, _;
78
+ let a = e.client.from(e.table).select(e.select ?? "*", { count: "exact" });
79
+ if (e.baseFilters)
80
+ for (const [u, f] of Object.entries(e.baseFilters))
81
+ a = a.eq(u, f);
82
+ if ((i = e.filterConditions) != null && i.value && (a = C(
83
+ a,
84
+ e.filterConditions.value,
85
+ ((g = e.filterLogic) == null ? void 0 : g.value) ?? "and"
86
+ )), (h = e.searchKeyword) != null && h.value && ((_ = e.searchColumns) != null && _.length)) {
87
+ const u = e.searchKeyword.value.trim();
88
+ if (u) {
89
+ const f = e.searchColumns.map((m) => `${m}.ilike.%${u}%`).join(",");
90
+ a = a.or(f);
91
+ }
92
+ }
93
+ o.sort.field && o.sort.order && (a = a.order(o.sort.field, {
94
+ ascending: o.sort.order === "asc"
95
+ }));
96
+ const s = (o.page - 1) * o.pageSize, c = s + o.pageSize - 1;
97
+ return a = a.range(s, c), a;
98
+ }
99
+ async function v(o) {
100
+ n.value = !0, t.value = null;
101
+ try {
102
+ const s = await l(o);
103
+ if (s.error) throw new Error(s.error.message);
104
+ const c = s.data ?? [], i = e.transform ? c.map(e.transform) : c;
105
+ return r.value = s.count ?? i.length, {
106
+ data: i,
107
+ total: r.value
108
+ };
109
+ } catch (a) {
110
+ return t.value = a, { data: [], total: 0 };
111
+ } finally {
112
+ n.value = !1;
113
+ }
114
+ }
115
+ return {
116
+ onFetch: v,
117
+ totalCount: $(r),
118
+ loading: $(n),
119
+ error: $(t),
120
+ /** Exposed for advanced usage */
121
+ buildQuery: l
122
+ };
123
+ }
124
+ export {
125
+ p as useSupabaseProvider
126
+ };
@@ -59,4 +59,7 @@ export declare function useTable<T extends Record<string, unknown>>(options?: Us
59
59
  refresh: () => void;
60
60
  fetchData: () => Promise<void>;
61
61
  setData: (data: T[]) => void;
62
+ patchRow: (rowId: string, fields: Partial<Record<string, unknown>>) => void;
63
+ insertRow: (row: T, position?: number) => void;
64
+ removeRows: (rowIds: (string | number)[]) => void;
62
65
  };
@@ -1,115 +1,135 @@
1
- import { shallowRef as U, ref as f, computed as d, readonly as v, triggerRef as z } from "vue";
2
- let b = !1;
3
- function j(u = {}) {
4
- const i = U(u.data ?? []), g = f(!1), S = f(null), n = f(/* @__PURE__ */ new Set()), l = f({ field: null, order: null }), a = f({
1
+ import { shallowRef as B, ref as d, computed as S, readonly as f, triggerRef as g } from "vue";
2
+ let D = !1;
3
+ function H(u = {}) {
4
+ const r = B(u.data ?? []), h = d(!1), p = d(null), i = d(/* @__PURE__ */ new Set()), n = d({ field: null, order: null }), t = d({
5
5
  page: 1,
6
6
  pageSize: u.pageSize ?? 20,
7
7
  total: (u.data ?? []).length
8
8
  });
9
- function D(e) {
10
- l.value.field === e ? (l.value.order = l.value.order === "asc" ? "desc" : l.value.order === "desc" ? null : "asc", l.value.order === null && (l.value.field = null)) : (l.value.field = e, l.value.order = "asc"), a.value.page = 1, n.value.clear(), u.serverSide && o();
11
- }
12
- function R(e, t) {
13
- l.value = { field: e, order: t }, a.value.page = 1, n.value.clear(), u.serverSide && o();
14
- }
15
- function M(e) {
16
- if (!l.value.field || !l.value.order) return e;
17
- const t = l.value.field, r = l.value.order;
18
- return [...e].sort((K, O) => {
19
- const s = K[t], c = O[t];
20
- if (s == null && c == null) return 0;
9
+ function x(e) {
10
+ n.value.field === e ? (n.value.order = n.value.order === "asc" ? "desc" : n.value.order === "desc" ? null : "asc", n.value.order === null && (n.value.field = null)) : (n.value.field = e, n.value.order = "asc"), t.value.page = 1, i.value.clear(), u.serverSide && c();
11
+ }
12
+ function M(e, l) {
13
+ n.value = { field: e, order: l }, t.value.page = 1, i.value.clear(), u.serverSide && c();
14
+ }
15
+ function P(e) {
16
+ if (!n.value.field || !n.value.order) return e;
17
+ const l = n.value.field, a = n.value.order;
18
+ return [...e].sort((o, y) => {
19
+ const s = o[l], v = y[l];
20
+ if (s == null && v == null) return 0;
21
21
  if (s == null) return 1;
22
- if (c == null) return -1;
23
- if (typeof s == "number" && typeof c == "number")
24
- return r === "asc" ? s - c : c - s;
25
- const m = String(s), y = String(c);
26
- return r === "asc" ? m.localeCompare(y) : y.localeCompare(m);
22
+ if (v == null) return -1;
23
+ if (typeof s == "number" && typeof v == "number")
24
+ return a === "asc" ? s - v : v - s;
25
+ const R = String(s), b = String(v);
26
+ return a === "asc" ? R.localeCompare(b) : b.localeCompare(R);
27
27
  });
28
28
  }
29
- function P(e) {
30
- a.value.page = Math.max(1, Math.min(e, w.value)), u.serverSide && o();
29
+ function C(e) {
30
+ t.value.page = Math.max(1, Math.min(e, z.value)), u.serverSide && c();
31
31
  }
32
- function x(e) {
33
- a.value.pageSize = e, a.value.page = 1, u.serverSide && o();
34
- }
35
- const w = d(
36
- () => Math.max(1, Math.ceil(a.value.total / a.value.pageSize))
37
- ), h = d(() => {
38
- if (u.serverSide) return i.value;
39
- const e = M(i.value);
40
- a.value.total = e.length;
41
- const { page: t, pageSize: r } = a.value;
42
- return e.slice((t - 1) * r, t * r);
32
+ function A(e) {
33
+ t.value.pageSize = e, t.value.page = 1, u.serverSide && c();
34
+ }
35
+ const z = S(
36
+ () => Math.max(1, Math.ceil(t.value.total / t.value.pageSize))
37
+ ), w = S(() => {
38
+ if (u.serverSide) return r.value;
39
+ const e = P(r.value);
40
+ t.value.total = e.length;
41
+ const { page: l, pageSize: a } = t.value;
42
+ return e.slice((l - 1) * a, l * a);
43
43
  });
44
- function p(e, t) {
45
- const r = e.id;
46
- return r == null && !b && (b = !0, console.warn(
44
+ function m(e, l) {
45
+ const a = e.id;
46
+ return a == null && !D && (D = !0, console.warn(
47
47
  "[OneUI] DataTable: row is missing an `id` field. Row selection may behave incorrectly after sorting or filtering. Please ensure each row has a unique `id` property."
48
- )), r ?? t;
48
+ )), a ?? l;
49
49
  }
50
- function C(e, t) {
51
- const r = p(e, t);
52
- n.value.has(r) ? n.value.delete(r) : n.value.add(r);
50
+ function F(e, l) {
51
+ const a = m(e, l);
52
+ i.value.has(a) ? i.value.delete(a) : i.value.add(a);
53
53
  }
54
- function A(e) {
55
- n.value.size === e.length ? n.value.clear() : e.forEach((t, r) => n.value.add(p(t, r)));
54
+ function I(e) {
55
+ i.value.size === e.length ? i.value.clear() : e.forEach((l, a) => i.value.add(m(l, a)));
56
56
  }
57
- function F(e, t) {
58
- return n.value.has(p(e, t));
57
+ function T(e, l) {
58
+ return i.value.has(m(e, l));
59
59
  }
60
- function I() {
61
- n.value.clear();
60
+ function k() {
61
+ i.value.clear();
62
62
  }
63
- const T = d(() => n.value.size), k = d(
64
- () => h.value.length > 0 && n.value.size === h.value.length
63
+ const q = S(() => i.value.size), E = S(
64
+ () => w.value.length > 0 && i.value.size === w.value.length
65
65
  );
66
- async function o() {
66
+ async function c() {
67
67
  if (u.onFetch) {
68
- g.value = !0, S.value = null;
68
+ h.value = !0, p.value = null;
69
69
  try {
70
70
  const e = await u.onFetch({
71
- page: a.value.page,
72
- pageSize: a.value.pageSize,
73
- sort: l.value
71
+ page: t.value.page,
72
+ pageSize: t.value.pageSize,
73
+ sort: n.value
74
74
  });
75
- i.value = e.data, z(i), a.value.total = e.total;
75
+ r.value = e.data, g(r), t.value.total = e.total;
76
76
  } catch (e) {
77
- S.value = e;
77
+ p.value = e;
78
78
  } finally {
79
- g.value = !1;
79
+ h.value = !1;
80
80
  }
81
81
  }
82
82
  }
83
- function q() {
84
- u.serverSide ? o() : a.value.page = 1;
83
+ function K() {
84
+ u.serverSide ? c() : t.value.page = 1;
85
+ }
86
+ function O(e) {
87
+ r.value = e, g(r), t.value.page = 1, u.serverSide || (t.value.total = e.length);
88
+ }
89
+ function U(e, l) {
90
+ const a = r.value, o = a.findIndex((y) => y.id === e);
91
+ o !== -1 && (a[o] = { ...a[o], ...l }, g(r));
92
+ }
93
+ function _(e, l) {
94
+ const a = r.value;
95
+ l !== void 0 && l >= 0 && l <= a.length ? a.splice(l, 0, e) : a.push(e), g(r), u.serverSide || (t.value.total = a.length);
85
96
  }
86
- function E(e) {
87
- i.value = e, z(i), a.value.page = 1, u.serverSide || (a.value.total = e.length);
97
+ function j(e) {
98
+ const l = new Set(e.map(String)), a = r.value;
99
+ r.value = a.filter(
100
+ (o) => !l.has(String(o.id))
101
+ ), g(r);
102
+ for (const o of e)
103
+ i.value.delete(o);
104
+ u.serverSide || (t.value.total = r.value.length);
88
105
  }
89
106
  return {
90
- data: h,
91
- rawData: v(i),
92
- loading: v(g),
93
- error: v(S),
94
- sort: v(l),
95
- pagination: v(a),
96
- totalPages: w,
97
- selectedRows: v(n),
98
- selectedCount: T,
99
- isAllSelected: k,
100
- toggleSort: D,
101
- setSort: R,
102
- setPage: P,
103
- setPageSize: x,
104
- toggleRowSelection: C,
105
- toggleSelectAll: A,
106
- isRowSelected: F,
107
- clearSelection: I,
108
- refresh: q,
109
- fetchData: o,
110
- setData: E
107
+ data: w,
108
+ rawData: f(r),
109
+ loading: f(h),
110
+ error: f(p),
111
+ sort: f(n),
112
+ pagination: f(t),
113
+ totalPages: z,
114
+ selectedRows: f(i),
115
+ selectedCount: q,
116
+ isAllSelected: E,
117
+ toggleSort: x,
118
+ setSort: M,
119
+ setPage: C,
120
+ setPageSize: A,
121
+ toggleRowSelection: F,
122
+ toggleSelectAll: I,
123
+ isRowSelected: T,
124
+ clearSelection: k,
125
+ refresh: K,
126
+ fetchData: c,
127
+ setData: O,
128
+ patchRow: U,
129
+ insertRow: _,
130
+ removeRows: j
111
131
  };
112
132
  }
113
133
  export {
114
- j as useTable
134
+ H as useTable
115
135
  };
@@ -1,12 +1,25 @@
1
1
  import { Ref } from 'vue';
2
+ import { AggregationConfig } from '../types';
3
+ export interface GroupConfig {
4
+ fieldId: string;
5
+ }
2
6
  export interface UseTableGroupOptions<T> {
3
7
  data: Ref<T[]>;
4
- groupBy: Ref<string | undefined>;
8
+ /** Single groupBy field (backwards-compatible) */
9
+ groupBy?: Ref<string | undefined>;
10
+ /** Multi-level group configs (takes priority over groupBy) */
11
+ groups?: Ref<GroupConfig[] | undefined>;
12
+ /** Aggregation configs applied to each group */
13
+ aggregations?: Ref<AggregationConfig[] | undefined>;
5
14
  }
6
15
  export type GroupHeaderItem = {
7
16
  __type: "group-header";
8
17
  __groupKey: string;
18
+ /** Full path for nested groups, e.g. ['status:done', 'priority:P0'] */
19
+ __groupPath: string[];
20
+ __groupLevel: number;
9
21
  __groupCount: number;
22
+ __aggregations?: Record<string, number>;
10
23
  id: string;
11
24
  };
12
25
  export type GroupedListItem<T> = (T & {
@@ -1,46 +1,70 @@
1
- import { ref as g, watch as w, computed as h } from "vue";
2
- function y(a) {
3
- const u = g(/* @__PURE__ */ new Set());
4
- function l(e) {
5
- const t = new Set(u.value);
6
- t.has(e) ? t.delete(e) : t.add(e), u.value = t;
1
+ import { ref as G, computed as h, watch as I } from "vue";
2
+ import { computeAggregations as L } from "../utils/aggregation.js";
3
+ function b(r) {
4
+ const n = G(/* @__PURE__ */ new Set());
5
+ function p(e) {
6
+ const t = new Set(n.value);
7
+ t.has(e) ? t.delete(e) : t.add(e), n.value = t;
7
8
  }
8
- w(a.groupBy, () => {
9
- u.value = /* @__PURE__ */ new Set();
9
+ const f = h(() => {
10
+ var t, s;
11
+ if ((t = r.groups) != null && t.value && r.groups.value.length > 0)
12
+ return r.groups.value;
13
+ const e = (s = r.groupBy) == null ? void 0 : s.value;
14
+ return e ? [{ fieldId: e }] : [];
15
+ });
16
+ I(f, () => {
17
+ n.value = /* @__PURE__ */ new Set();
10
18
  });
11
19
  const d = h(() => {
12
- const e = a.data.value, t = a.groupBy.value;
13
- if (!t)
20
+ var _;
21
+ const e = r.data.value, t = f.value;
22
+ if (t.length === 0)
14
23
  return e.map((o) => ({ ...o, __type: "data-row" }));
15
- const c = [], s = /* @__PURE__ */ new Map();
16
- for (const o of e) {
17
- const n = o[t], r = n != null ? String(n) : "";
18
- s.has(r) || (c.push(r), s.set(r, [])), s.get(r).push(o);
19
- }
20
- const p = [], _ = u.value;
21
- for (const o of c) {
22
- const n = s.get(o);
23
- if (p.push({
24
- __type: "group-header",
25
- __groupKey: o,
26
- __groupCount: n.length,
27
- id: `__group__${o}`
28
- }), !_.has(o))
29
- for (const r of n)
30
- p.push({ ...r, __type: "data-row" });
31
- }
32
- return p;
24
+ const s = ((_ = r.aggregations) == null ? void 0 : _.value) ?? [], l = n.value, a = [];
25
+ return v(e, t, 0, [], l, s, a), a;
33
26
  });
34
- function f(e) {
27
+ function g(e) {
35
28
  return e && e.__type === "group-header";
36
29
  }
37
30
  return {
38
31
  groupedItems: d,
39
- collapsedGroups: u,
40
- toggleGroup: l,
41
- isGroupHeader: f
32
+ collapsedGroups: n,
33
+ toggleGroup: p,
34
+ isGroupHeader: g
42
35
  };
43
36
  }
37
+ function v(r, n, p, f, d, g, e) {
38
+ const t = n[p];
39
+ if (!t) {
40
+ for (const o of r)
41
+ e.push({ ...o, __type: "data-row" });
42
+ return;
43
+ }
44
+ const s = t.fieldId, l = [], a = /* @__PURE__ */ new Map();
45
+ for (const o of r) {
46
+ const c = o[s], u = c != null ? String(c) : "";
47
+ a.has(u) || (l.push(u), a.set(u, [])), a.get(u).push(o);
48
+ }
49
+ const _ = p === n.length - 1;
50
+ for (const o of l) {
51
+ const c = a.get(o), u = [...f, `${s}:${o}`], i = u.join("/"), w = g.length > 0 ? L(c, g) : void 0, y = {
52
+ __type: "group-header",
53
+ __groupKey: o,
54
+ __groupPath: u,
55
+ __groupLevel: p,
56
+ __groupCount: c.length,
57
+ __aggregations: w,
58
+ id: `__group__${i}`
59
+ };
60
+ if (e.push(y), !d.has(i))
61
+ if (_)
62
+ for (const m of c)
63
+ e.push({ ...m, __type: "data-row" });
64
+ else
65
+ v(c, n, p + 1, u, d, g, e);
66
+ }
67
+ }
44
68
  export {
45
- y as useTableGroup
69
+ b as useTableGroup
46
70
  };
@@ -0,0 +1,98 @@
1
+ import { ViewConfig } from '../types';
2
+ export interface ViewStorageBackend {
3
+ /** 加载指定表的所有视图 */
4
+ load(tableId: string): Promise<ViewConfig[]>;
5
+ /** 保存/更新视图 */
6
+ save(tableId: string, view: ViewConfig): Promise<void>;
7
+ /** 删除视图 */
8
+ remove(tableId: string, viewId: string): Promise<void>;
9
+ }
10
+ export declare function createLocalStorageBackend(): ViewStorageBackend;
11
+ export interface SupabaseViewBackendOptions {
12
+ /** Supabase client (最小接口:from().select/insert/update/delete) */
13
+ client: {
14
+ from(table: string): any;
15
+ };
16
+ /** 存储视图配置的表名,默认 'view_configs' */
17
+ tableName?: string;
18
+ }
19
+ export declare function createSupabaseBackend(options: SupabaseViewBackendOptions): ViewStorageBackend;
20
+ export interface UseViewPersistenceOptions {
21
+ /** 表 ID */
22
+ tableId: string;
23
+ /** 存储后端,默认 localStorage */
24
+ backend?: ViewStorageBackend;
25
+ /** 默认视图(当没有保存的视图时使用) */
26
+ defaultView?: ViewConfig;
27
+ /** 是否自动加载(默认 true) */
28
+ autoLoad?: boolean;
29
+ }
30
+ export declare function useViewPersistence(options: UseViewPersistenceOptions): {
31
+ views: Readonly<import('vue').Ref<readonly {
32
+ readonly viewId: string;
33
+ readonly viewType: import('..').ViewType;
34
+ readonly name: string;
35
+ readonly visibleFields: readonly string[];
36
+ readonly sorts?: readonly {
37
+ readonly fieldId: string;
38
+ readonly direction: "asc" | "desc";
39
+ }[] | undefined;
40
+ readonly groups?: readonly {
41
+ readonly fieldId: string;
42
+ }[] | undefined;
43
+ readonly filters?: readonly {
44
+ readonly fieldId: string;
45
+ readonly operator: "eq" | "neq" | "contains" | "gt" | "lt" | "is_empty" | "is_not_empty";
46
+ readonly value?: string | number | boolean | readonly string[] | null | undefined;
47
+ }[] | undefined;
48
+ readonly kanbanFieldId?: string | undefined;
49
+ readonly galleryCoverFieldId?: string | undefined;
50
+ readonly galleryCardFields?: readonly string[] | undefined;
51
+ readonly aggregations?: readonly {
52
+ readonly fieldId: string;
53
+ readonly fn: import('..').AggregationFn;
54
+ }[] | undefined;
55
+ readonly fixedColumns?: readonly string[] | undefined;
56
+ }[], readonly {
57
+ readonly viewId: string;
58
+ readonly viewType: import('..').ViewType;
59
+ readonly name: string;
60
+ readonly visibleFields: readonly string[];
61
+ readonly sorts?: readonly {
62
+ readonly fieldId: string;
63
+ readonly direction: "asc" | "desc";
64
+ }[] | undefined;
65
+ readonly groups?: readonly {
66
+ readonly fieldId: string;
67
+ }[] | undefined;
68
+ readonly filters?: readonly {
69
+ readonly fieldId: string;
70
+ readonly operator: "eq" | "neq" | "contains" | "gt" | "lt" | "is_empty" | "is_not_empty";
71
+ readonly value?: string | number | boolean | readonly string[] | null | undefined;
72
+ }[] | undefined;
73
+ readonly kanbanFieldId?: string | undefined;
74
+ readonly galleryCoverFieldId?: string | undefined;
75
+ readonly galleryCardFields?: readonly string[] | undefined;
76
+ readonly aggregations?: readonly {
77
+ readonly fieldId: string;
78
+ readonly fn: import('..').AggregationFn;
79
+ }[] | undefined;
80
+ readonly fixedColumns?: readonly string[] | undefined;
81
+ }[]>>;
82
+ activeView: import('vue').ComputedRef<ViewConfig>;
83
+ activeViewId: import('vue').Ref<string, string>;
84
+ viewList: import('vue').ComputedRef<{
85
+ id: string;
86
+ name: string;
87
+ type: import('..').ViewType;
88
+ }[]>;
89
+ loading: Readonly<import('vue').Ref<boolean, boolean>>;
90
+ error: Readonly<import('vue').Ref<Error | null, Error | null>>;
91
+ loadViews: () => Promise<void>;
92
+ saveView: (config?: Partial<ViewConfig>) => Promise<void>;
93
+ createView: (name: string, baseConfig?: Partial<ViewConfig>) => Promise<string>;
94
+ deleteView: (viewId: string) => Promise<void>;
95
+ switchView: (viewId: string) => void;
96
+ duplicateView: (sourceViewId: string, newName: string) => Promise<string>;
97
+ updateActiveView: (patch: Partial<ViewConfig>) => void;
98
+ };