@meistrari/tela-build 1.53.0 → 1.54.0

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.
@@ -31,7 +31,7 @@ const props = defineProps<{
31
31
  }>()
32
32
 
33
33
  const emit = defineEmits<{
34
- select: [id: string]
34
+ select: [id: string, shiftKey: boolean]
35
35
  open: [id: string]
36
36
  }>()
37
37
 
@@ -87,10 +87,18 @@ function shouldEnableRowClick() {
87
87
  return props.hoverMechanism === HoverMechanismTypes.Row && !props.disableRowClick
88
88
  }
89
89
 
90
+ // Captured in the capture phase (before the checkbox toggles) so range-select
91
+ // with Shift can be resolved by the parent table.
92
+ const shiftHeldOnSelect = ref(false)
93
+ function captureSelectModifier(event: MouseEvent) {
94
+ shiftHeldOnSelect.value = event.shiftKey
95
+ }
96
+
90
97
  function handleCheckboxChange() {
91
98
  if (props.row.id) {
92
- emit('select', props.row.id)
99
+ emit('select', props.row.id, shiftHeldOnSelect.value)
93
100
  }
101
+ shiftHeldOnSelect.value = false
94
102
  }
95
103
  </script>
96
104
 
@@ -100,7 +108,7 @@ function handleCheckboxChange() {
100
108
  :class="computedRowClass"
101
109
  @click.stop="handleRowClick"
102
110
  >
103
- <td v-if="allowSelect" class="sticky left-0 bg-white z-9" :class="[selectClass]" @click.stop>
111
+ <td v-if="allowSelect" class="sticky left-0 bg-white z-9" :class="[selectClass]" @click.capture="captureSelectModifier" @click.stop>
104
112
  <div
105
113
  flex="~" h-px items-center justify-center w-32px relative z-0
106
114
  >
@@ -36,6 +36,7 @@ const {
36
36
  isAllSelected,
37
37
  selectAll,
38
38
  toggleRow,
39
+ setSelectedRows,
39
40
  } = useTableSelection({
40
41
  rows: computed(() => props.rows),
41
42
  initialSelectedRows: props.selectedRows,
@@ -57,6 +58,7 @@ const {
57
58
  isAllSelected,
58
59
  selectAll,
59
60
  toggleRow,
61
+ setSelectedRows,
60
62
  })
61
63
 
62
64
  const hoverMechanism = computed(() => props.hoverMechanism || HoverMechanismTypes.Cell)
@@ -42,6 +42,7 @@ const {
42
42
  isAllSelected,
43
43
  selectAll,
44
44
  toggleRow,
45
+ setSelectedRows,
45
46
  } = useTableSelection({
46
47
  rows: computed(() => props.rows),
47
48
  initialSelectedRows: props.selectedRows,
@@ -63,6 +64,7 @@ const {
63
64
  isAllSelected,
64
65
  selectAll,
65
66
  toggleRow,
67
+ setSelectedRows,
66
68
  })
67
69
 
68
70
  const hoverMechanism = computed(() => props.hoverMechanism || HoverMechanismTypes.Cell)
@@ -19,23 +19,60 @@ export interface UseTableCommonOptions {
19
19
  isAllSelected: Ref<boolean>
20
20
  selectAll: (value: boolean) => string[]
21
21
  toggleRow: (id: string) => string[]
22
+ setSelectedRows?: (ids: string[]) => void
22
23
  }
23
24
 
24
25
  export function useTableCommon(options: UseTableCommonOptions) {
25
- const { props, emit, mainTableEl, selectedRows, isAllSelected, selectAll, toggleRow } = options
26
+ const { props, emit, mainTableEl, selectedRows, isAllSelected, selectAll, toggleRow, setSelectedRows } = options
26
27
 
27
28
  const hasHorizontalScroll = ref(false)
29
+ // Anchor for Shift range-select: the last row toggled without Shift.
30
+ const selectionAnchor = ref<string | null>(null)
31
+ // Rows selected by the last Shift range-select; the next Shift-click from
32
+ // the same anchor replaces them instead of accumulating (Gmail/Finder style).
33
+ const lastShiftRange = ref<string[]>([])
28
34
 
29
35
  function shouldEnableRowClick(rowData: Row) {
30
36
  return props.hoverMechanism === HoverMechanismTypes.Row && !props.disableRowClick?.(rowData)
31
37
  }
32
38
 
33
- function handleRowSelect(rowId: string) {
39
+ function selectableRowIds(): string[] {
40
+ return props.rows.map((row: Row) => row?.id).filter(Boolean) as string[]
41
+ }
42
+
43
+ function rangeBetween(anchorId: string, targetId: string): string[] | null {
44
+ const ids = selectableRowIds()
45
+ const from = ids.indexOf(anchorId)
46
+ const to = ids.indexOf(targetId)
47
+ if (from === -1 || to === -1)
48
+ return null
49
+ const [lo, hi] = from <= to ? [from, to] : [to, from]
50
+ return ids.slice(lo, hi + 1)
51
+ }
52
+
53
+ function handleRowSelect(rowId: string, shiftKey = false) {
54
+ if (shiftKey && selectionAnchor.value && selectionAnchor.value !== rowId) {
55
+ const range = rangeBetween(selectionAnchor.value, rowId)
56
+ if (range) {
57
+ const previousRange = new Set(lastShiftRange.value)
58
+ const kept = selectedRows.value.filter((id: string) => !previousRange.has(id))
59
+ const newSelection = Array.from(new Set([...kept, ...range]))
60
+ lastShiftRange.value = range
61
+ if (!props.selectedRows)
62
+ setSelectedRows?.(newSelection)
63
+ emit('select', newSelection, newSelection.length === selectableRowIds().length)
64
+ return
65
+ }
66
+ }
67
+
68
+ selectionAnchor.value = rowId
69
+ lastShiftRange.value = []
70
+
34
71
  if (props.selectedRows) {
35
72
  const newSelection = selectedRows.value.includes(rowId)
36
73
  ? selectedRows.value.filter((id: string) => id !== rowId)
37
74
  : [...selectedRows.value, rowId]
38
- emit('select', newSelection, newSelection.length === props.rows.length)
75
+ emit('select', newSelection, newSelection.length === selectableRowIds().length)
39
76
  return
40
77
  }
41
78
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/tela-build",
3
- "version": "1.53.0",
3
+ "version": "1.54.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "app.config.ts",