@xy-planning-network/trees 0.15.1 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xy-planning-network/trees",
3
- "version": "0.15.1",
3
+ "version": "0.15.2",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,7 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  import { useTable } from "@/composables/useTable"
3
+ import { useBulkActions } from "@/composables/useBulkActions"
3
4
  import type {
4
5
  TableActions,
6
+ TableBulkActions,
5
7
  TableColumns,
6
8
  TableRowsData,
7
9
  } from "@/composables/table"
@@ -11,19 +13,44 @@ import { toRef } from "vue"
11
13
  const props = withDefaults(
12
14
  defineProps<{
13
15
  tableActions?: TableActions<any>
16
+ tableBulkActions?: TableBulkActions<any>
14
17
  tableColumns: TableColumns<any>
15
18
  tableData: TableRowsData
16
19
  }>(),
17
20
  {
18
21
  tableActions: () => ({ type: "dropdown", actions: [] }),
22
+ tableBulkActions: () => ({ actions: [] }),
19
23
  }
20
24
  )
21
25
 
26
+ const selected = defineModel<number[]>("selected", {
27
+ required: false,
28
+ default: [],
29
+ })
30
+
31
+ const {
32
+ bulkActions,
33
+ bulkSelectChecked,
34
+ bulkSelectIndeterminate,
35
+ bulkSelectOnChange,
36
+ hasBulkActions,
37
+ publicMethods,
38
+ selectable,
39
+ selectedOnPage,
40
+ } = useBulkActions(
41
+ toRef(props, "tableData"),
42
+ toRef(props, "tableBulkActions"),
43
+ selected
44
+ )
45
+
22
46
  const { columns, hasActions, isEmptyCellValue, rows } = useTable(
23
47
  toRef(props, "tableData"),
24
48
  toRef(props, "tableColumns"),
25
- toRef(props, "tableActions")
49
+ toRef(props, "tableActions"),
50
+ publicMethods
26
51
  )
52
+
53
+ defineExpose(publicMethods)
27
54
  </script>
28
55
  <template>
29
56
  <div class="flex flex-col">
@@ -35,6 +62,25 @@ const { columns, hasActions, isEmptyCellValue, rows } = useTable(
35
62
  <table class="min-w-full divide-y divide-gray-200">
36
63
  <thead class="bg-gray-100">
37
64
  <tr>
65
+ <th
66
+ v-if="hasBulkActions"
67
+ scope="col"
68
+ class="pl-6 w-4 leading-none"
69
+ >
70
+ <input
71
+ :class="[
72
+ 'h-4 w-4 rounded cursor-pointer',
73
+ 'disabled:bg-gray-100 disabled:border-gray-200 disabled:cursor-not-allowed disabled:opacity-100',
74
+ 'checked:disabled:bg-xy-blue checked:disabled:border-xy-blue checked:disabled:opacity-50',
75
+ 'border-gray-300 focus:ring-xy-blue-500',
76
+ ]"
77
+ :checked="bulkSelectChecked"
78
+ :indeterminate="bulkSelectIndeterminate"
79
+ type="checkbox"
80
+ @change="bulkSelectOnChange"
81
+ />
82
+ </th>
83
+
38
84
  <th
39
85
  v-for="(col, idx) in columns"
40
86
  :key="idx"
@@ -50,6 +96,23 @@ const { columns, hasActions, isEmptyCellValue, rows } = useTable(
50
96
  class="px-6 py-3 text-xs font-medium tracking-wider text-gray-900 uppercase leading-4"
51
97
  />
52
98
  </tr>
99
+
100
+ <tr v-if="hasBulkActions && selected.length > 0">
101
+ <td colspan="100%" class="px-6 py-2.5 border-t bg-neutral-50">
102
+ <div class="flex items-center gap-x-3">
103
+ <div class="text-sm shrink-0">
104
+ Selected
105
+ <span class="font-medium">{{
106
+ selectedOnPage.length
107
+ }}</span>
108
+ of
109
+ <span class="font-medium">{{ selectable.length }}</span>
110
+ </div>
111
+
112
+ <ActionsButtonGroup :actions="bulkActions" />
113
+ </div>
114
+ </td>
115
+ </tr>
53
116
  </thead>
54
117
  <tbody class="bg-white">
55
118
  <tr
@@ -57,6 +120,21 @@ const { columns, hasActions, isEmptyCellValue, rows } = useTable(
57
120
  :key="rowIdx"
58
121
  class="even:bg-gray-50"
59
122
  >
123
+ <td v-if="hasBulkActions" class="pl-6 w-4 leading-none">
124
+ <input
125
+ v-model="selected"
126
+ :class="[
127
+ 'h-4 w-4 rounded cursor-pointer',
128
+ 'disabled:bg-gray-100 disabled:border-gray-200 disabled:cursor-not-allowed disabled:opacity-100',
129
+ 'checked:disabled:bg-xy-blue checked:disabled:border-xy-blue checked:disabled:opacity-50',
130
+ 'border-gray-300 focus:ring-xy-blue-500',
131
+ ]"
132
+ :disabled="!selectable.includes(row.rowData?.id)"
133
+ type="checkbox"
134
+ :value="row.rowData?.id"
135
+ />
136
+ </td>
137
+
60
138
  <template v-for="(cell, cellIdx) in row.cells" :key="cellIdx">
61
139
  <component
62
140
  :is="'td'"
@@ -89,7 +167,11 @@ const { columns, hasActions, isEmptyCellValue, rows } = useTable(
89
167
 
90
168
  <tr v-if="rows.length === 0">
91
169
  <td
92
- :colspan="columns.length + (hasActions ? 1 : 0)"
170
+ :colspan="
171
+ columns.length +
172
+ (hasActions ? 1 : 0) +
173
+ (hasBulkActions ? 1 : 0)
174
+ "
93
175
  class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap leading-5"
94
176
  >
95
177
  No items were found!
@@ -8,7 +8,6 @@ import {
8
8
  import DateRangePicker from "../forms/DateRangePicker.vue"
9
9
  import BaseAPI from "../../api/base"
10
10
  import type {
11
- DynamicTableAPI,
12
11
  DynamicTableOptions,
13
12
  TableActions,
14
13
  TableBulkActions,
@@ -19,6 +18,7 @@ import { useAppFlasher } from "@/composables/useFlashes"
19
18
  import { TrailsRespPaged } from "@/api/client"
20
19
  import { DateRange, DateRangeProps } from "@/composables/date"
21
20
  import { useTable } from "@/composables/useTable"
21
+ import { useBulkActions } from "@/composables/useBulkActions"
22
22
 
23
23
  const props = withDefaults(
24
24
  defineProps<{
@@ -141,100 +141,25 @@ const hasContent = computed((): boolean => {
141
141
  return rows.value.length ? true : false
142
142
  })
143
143
 
144
- const clearSelections = () => {
145
- selected.value = []
146
- }
147
-
148
144
  const selected = defineModel<number[]>("selected", {
149
145
  required: false,
150
146
  default: [],
151
147
  })
152
148
 
153
- const selectedData = computed((): TableRowData[] => {
154
- return tableData.value.filter((data) => {
155
- return selected.value.includes(data?.id)
156
- })
157
- })
158
-
159
- const selectedOnPage = computed(() => {
160
- return selected.value.filter((id) => {
161
- return selectable.value.includes(id)
162
- })
163
- })
164
-
165
- const bulkActions = computed(() => {
166
- return props.tableBulkActions.actions
167
- .filter((action) => {
168
- return action.show ?? true
169
- })
170
- .map((action) => {
171
- return {
172
- ...action,
173
- disabled: selected.value.length === 0 || action.disabled,
174
- onClick: (e?: Event) =>
175
- action.onClick.apply(undefined, [
176
- selected.value,
177
- selectedData.value,
178
- publicMethods,
179
- e,
180
- ]),
181
- }
182
- })
183
- })
184
-
185
- const hasBulkActions = computed(() => bulkActions.value.length > 0)
186
-
187
- const selectable = computed(() => {
188
- return tableData.value
189
- .filter((row) => {
190
- // NOTE(spk): table data must have an "id" key for bulk actions.
191
- if (row.id === undefined) {
192
- return false
193
- }
194
-
195
- if (props.tableBulkActions.isSelectable === undefined) {
196
- return true
197
- }
198
-
199
- if (props.tableBulkActions.isSelectable(row)) {
200
- return true
201
- }
202
-
203
- return false
204
- })
205
- .map((d) => d["id"])
206
- })
207
-
208
- const bulkSelectChecked = computed(
209
- () =>
210
- selectedOnPage.value.length > 0 &&
211
- selectedOnPage.value.length === selectable.value.length
212
- )
213
-
214
- const bulkSelectIndeterminate = computed(
215
- () =>
216
- selectedOnPage.value.length > 0 &&
217
- selectedOnPage.value.length < selectable.value.length
218
- )
219
-
220
- const bulkSelectOnChange = (e: Event) => {
221
- const isChecked = (e.target as HTMLInputElement).checked
222
-
223
- // set all records on current page to selection
224
- if (isChecked) {
225
- selected.value = selectable.value.map((id) => id)
226
- return
227
- }
228
-
229
- clearSelections()
230
- }
231
-
232
- const publicMethods: DynamicTableAPI = {
233
- clearSelection: clearSelections,
234
- selectedData: selectedData,
149
+ const {
150
+ bulkActions,
151
+ bulkSelectChecked,
152
+ bulkSelectIndeterminate,
153
+ bulkSelectOnChange,
154
+ clearSelections,
155
+ hasBulkActions,
156
+ publicMethods,
157
+ selectable,
158
+ selectedOnPage,
159
+ } = useBulkActions(tableData, toRef(props, "tableBulkActions"), selected, {
235
160
  refresh: loadAndRender,
236
161
  reset: reloadTable,
237
- }
162
+ })
238
163
 
239
164
  const { columns, hasActions, isEmptyCellValue, rows } = useTable(
240
165
  tableData,
@@ -489,7 +414,9 @@ loadAndRender()
489
414
 
490
415
  <tr v-if="!hasContent">
491
416
  <td
492
- :colspan="columns.length + (hasActions ? 1 : 0)"
417
+ :colspan="
418
+ columns.length + (hasActions ? 1 : 0) + (hasBulkActions ? 1 : 0)
419
+ "
493
420
  class="px-6 py-4 text-sm text-gray-700 whitespace-nowrap leading-5"
494
421
  >
495
422
  No items were found!
@@ -0,0 +1,25 @@
1
+ import { type Ref } from "vue";
2
+ import type { DynamicTableAPI, TableBulkActions, TableRowData } from "./table";
3
+ interface UseBulkActionsOptions {
4
+ refresh?: () => void;
5
+ reset?: () => void;
6
+ }
7
+ export declare const useBulkActions: <T extends TableRowData>(tableData: Ref<T[]>, tableBulkActions: Ref<TableBulkActions<T>>, selected: Ref<number[]>, options?: UseBulkActionsOptions) => {
8
+ bulkActions: import("vue").ComputedRef<{
9
+ disabled: boolean | undefined;
10
+ onClick: (e?: Event) => void;
11
+ label: string;
12
+ icon?: import("vue").RenderFunction | import("vue").FunctionalComponent<{}, {}, any, {}> | undefined;
13
+ show?: boolean | undefined;
14
+ }[]>;
15
+ bulkSelectChecked: import("vue").ComputedRef<boolean>;
16
+ bulkSelectIndeterminate: import("vue").ComputedRef<boolean>;
17
+ bulkSelectOnChange: (e: Event) => void;
18
+ clearSelections: () => void;
19
+ hasBulkActions: import("vue").ComputedRef<boolean>;
20
+ publicMethods: DynamicTableAPI<T>;
21
+ selectable: import("vue").ComputedRef<any[]>;
22
+ selectedData: import("vue").ComputedRef<T[]>;
23
+ selectedOnPage: import("vue").ComputedRef<number[]>;
24
+ };
25
+ export {};
@@ -1,6 +1,7 @@
1
1
  import { Ref } from "vue";
2
2
  import { TableColumns, TableRowsData, TableActions, DynamicTableAPI } from "./table";
3
3
  import { ActionItemButton, ActionItemLink } from "../entry";
4
+ export declare const tableAPIStub: DynamicTableAPI;
4
5
  export declare const useTable: (rowData: TableRowsData | Ref<TableRowsData>, cols: TableColumns | Ref<TableColumns>, acts: TableActions | Ref<TableActions>, exposedAPI?: DynamicTableAPI) => {
5
6
  columns: import("vue").ComputedRef<{
6
7
  alignment: string;
@@ -1,10 +1,24 @@
1
- import type { TableActions, TableColumns, TableRowsData } from "../../composables/table";
1
+ import type { TableActions, TableBulkActions, TableColumns, TableRowsData } from "../../composables/table";
2
2
  type __VLS_Props = {
3
3
  tableActions?: TableActions<any>;
4
+ tableBulkActions?: TableBulkActions<any>;
4
5
  tableColumns: TableColumns<any>;
5
6
  tableData: TableRowsData;
6
7
  };
7
- declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
8
+ type __VLS_PublicProps = {
9
+ "selected"?: number[];
10
+ } & __VLS_Props;
11
+ declare const _default: import("vue").DefineComponent<__VLS_PublicProps, {
12
+ clearSelection: () => void;
13
+ refresh: () => void;
14
+ reset: () => void;
15
+ selectedData: import("vue").ComputedRef<import("../../composables/table").TableRowData[]>;
16
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
17
+ "update:selected": (value: number[]) => any;
18
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
19
+ "onUpdate:selected"?: ((value: number[]) => any) | undefined;
20
+ }>, {
8
21
  tableActions: TableActions<any>;
22
+ tableBulkActions: TableBulkActions<any>;
9
23
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, HTMLDivElement>;
10
24
  export default _default;