@globalbrain/sefirot 4.14.1 → 4.14.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.
@@ -1,18 +1,10 @@
1
- <script setup lang="ts" generic="S extends any[] | any | undefined = undefined">
1
+ <script setup lang="ts" generic="S extends any = undefined">
2
2
  import { useVirtualizer } from '@tanstack/vue-virtual'
3
3
  import { useResizeObserver } from '@vueuse/core'
4
4
  import xor from 'lodash-es/xor'
5
- import {
6
- computed,
7
- nextTick,
8
- reactive,
9
- ref,
10
- shallowRef,
11
- toValue,
12
- unref,
13
- watch
14
- } from 'vue'
5
+ import { computed, nextTick, reactive, ref, shallowRef, toValue, unref, watch } from 'vue'
15
6
  import { type Table } from '../composables/Table'
7
+ import { smartComputed } from '../support/Reactivity'
16
8
  import { getTextWidth } from '../support/Text'
17
9
  import SInputCheckbox from './SInputCheckbox.vue'
18
10
  import SInputRadio from './SInputRadio.vue'
@@ -23,28 +15,24 @@ import STableFooter from './STableFooter.vue'
23
15
  import STableHeader from './STableHeader.vue'
24
16
  import STableItem from './STableItem.vue'
25
17
 
26
- const props = defineProps<{
27
- options: Table
28
- selected?: S
29
- }>()
30
-
31
- const emit = defineEmits<{
32
- (e: 'update:selected', value: S): void
33
- }>()
18
+ const props = defineProps<{ options: Table }>()
19
+ const selected = defineModel<S>('selected')
34
20
 
35
21
  const head = shallowRef<HTMLElement | null>(null)
36
22
  const body = shallowRef<HTMLElement | null>(null)
37
23
  const block = shallowRef<HTMLElement | null>(null)
38
24
  const row = shallowRef<HTMLElement | null>(null)
39
25
 
40
- const ordersToShow = computed(() => {
26
+ const ordersToShow = smartComputed(() => {
41
27
  const orders = unref(props.options.orders).filter((key) => {
42
28
  const show = unref(props.options.columns)[key]?.show
43
29
  return toValue(show) !== false
44
30
  })
45
- if (props.selected === undefined) {
31
+
32
+ if (selected.value === undefined) {
46
33
  return orders
47
34
  }
35
+
48
36
  return ['__select', ...orders]
49
37
  })
50
38
 
@@ -126,51 +114,45 @@ const recordsWithSummary = computed(() => {
126
114
  return summary ? [...records, summary] : records
127
115
  })
128
116
 
129
- const indexes = computed(() => {
130
- if (props.selected === undefined) {
117
+ const indexes = smartComputed(() => {
118
+ if (selected.value === undefined) {
131
119
  return []
132
120
  }
121
+
133
122
  const records = unref(props.options.records) ?? []
134
123
  const indexField = unref(props.options.indexField)
135
124
 
136
- return records.map((record, i) => indexField ? record[indexField] : i)
125
+ return records.map((record, i) => (indexField ? record[indexField] : i))
137
126
  })
138
127
 
139
- const selectedIndexes = reactive(new Set(Array.isArray(props.selected) ? props.selected : []))
140
-
141
128
  const control = computed({
142
129
  get() {
143
- if (Array.isArray(props.selected)) {
144
- return props.selected.length === indexes.value.length
145
- ? true
146
- : props.selected.length ? 'indeterminate' : false
130
+ if (Array.isArray(selected.value)) {
131
+ if (!selected.value.length) {
132
+ return false
133
+ }
134
+
135
+ if (selected.value.length === indexes.value.length) {
136
+ return true
137
+ }
147
138
  }
148
139
 
149
- return 'indeterminate' // doesn't matter
140
+ return 'indeterminate'
150
141
  },
151
142
 
152
143
  set(newValue) {
153
144
  if (newValue === false) {
154
- selectedIndexes.clear()
145
+ updateSelected([])
155
146
  } else if (newValue === true) {
156
- indexes.value.forEach((index) => {
157
- selectedIndexes.add(index)
158
- })
147
+ updateSelected(indexes.value)
159
148
  }
160
149
  }
161
150
  })
162
151
 
163
152
  watch(indexes, (newValue, oldValue) => {
164
- if (Array.isArray(props.selected)) {
165
- xor(newValue, oldValue).forEach((index) => {
166
- selectedIndexes.delete(index)
167
- })
168
- }
169
- })
170
-
171
- watch(selectedIndexes, (newValue) => {
172
- if (Array.isArray(props.selected)) {
173
- updateSelected(Array.from(newValue))
153
+ if (Array.isArray(selected.value)) {
154
+ const removed = xor(newValue, oldValue)
155
+ updateSelected(selected.value.filter((item) => !removed.includes(item)))
174
156
  }
175
157
  })
176
158
 
@@ -256,10 +238,9 @@ watch(actionsColumnWidth, (newValue) => {
256
238
 
257
239
  function stopObserving() {
258
240
  const orders = ordersToShow.value
259
- const lastOrder
260
- = orders[orders.length - 1] === 'actions'
261
- ? orders[orders.length - 2]
262
- : orders[orders.length - 1]
241
+ const lastOrder = orders[orders.length - 1] === 'actions'
242
+ ? orders[orders.length - 2]
243
+ : orders[orders.length - 1]
263
244
  colWidths[lastOrder] = 'auto'
264
245
  resizeObserver.stop()
265
246
  }
@@ -284,10 +265,7 @@ async function handleResize() {
284
265
  }
285
266
 
286
267
  const availableFill = row.value.getBoundingClientRect().width - totalWidth
287
- updateColWidth(
288
- nameOfColToGrow.value,
289
- `calc(${availableFill}px + ${initialWidth})`
290
- )
268
+ updateColWidth(nameOfColToGrow.value, `calc(${availableFill}px + ${initialWidth})`)
291
269
  }
292
270
 
293
271
  function syncHeadScroll() {
@@ -341,18 +319,24 @@ function getCell(key: string, index: number) {
341
319
  return { type: 'custom' }
342
320
  }
343
321
  const col = unref(props.options.columns)[key]
344
- return (isSummary(index) && col?.summaryCell) ? col?.summaryCell : col?.cell
322
+ return isSummary(index) && col?.summaryCell ? col?.summaryCell : col?.cell
345
323
  }
346
324
 
347
- function updateSelected(selected: any) {
348
- if (Array.isArray(props.selected)) {
349
- if (xor(selected, props.selected ?? []).length) {
350
- emit('update:selected', selected)
351
- }
325
+ function updateSelected(items: any) {
326
+ if (Array.isArray(selected.value)) {
327
+ selected.value = [...items] as any
352
328
  } else {
353
- emit('update:selected', selected)
329
+ selected.value = items
354
330
  }
355
331
  }
332
+
333
+ function addSelected(item: any) {
334
+ updateSelected([...(selected.value as any), item])
335
+ }
336
+
337
+ function removeSelected(item: any) {
338
+ updateSelected((selected.value as any[]).filter((i) => i !== item))
339
+ }
356
340
  </script>
357
341
 
358
342
  <template>
@@ -369,11 +353,7 @@ function updateSelected(selected: any) {
369
353
  />
370
354
 
371
355
  <div class="table" role="grid">
372
- <div
373
- class="container head"
374
- ref="head"
375
- @scroll="syncHeadScroll"
376
- >
356
+ <div class="container head" ref="head" @scroll="syncHeadScroll">
377
357
  <div class="block" ref="block">
378
358
  <div class="row" ref="row">
379
359
  <STableItem
@@ -421,10 +401,7 @@ function updateSelected(selected: any) {
421
401
  position: 'relative'
422
402
  }"
423
403
  >
424
- <div
425
- v-for="{ index, key: __key, size, start } in virtualItems"
426
- :key="__key"
427
- >
404
+ <div v-for="{ index, key: __key, size, start } in virtualItems" :key="__key">
428
405
  <div
429
406
  class="row"
430
407
  :class="isSummaryOrLastClass(index)"
@@ -456,14 +433,14 @@ function updateSelected(selected: any) {
456
433
  <template v-if="key === '__select' && !isSummary(index)">
457
434
  <SInputCheckbox
458
435
  v-if="Array.isArray(selected)"
459
- :model-value="selectedIndexes.has(indexes[index])"
460
- @update:model-value="c => selectedIndexes[c ? 'add' : 'delete'](indexes[index])"
436
+ :model-value="selected.includes(indexes[index])"
437
+ @update:model-value="(c) => (c ? addSelected : removeSelected)(indexes[index])"
461
438
  :disabled="options.disableSelection?.(recordsWithSummary[index]) === true"
462
439
  />
463
440
  <SInputRadio
464
441
  v-else
465
442
  :model-value="selected === indexes[index]"
466
- @update:model-value="c => updateSelected(c ? indexes[index] : null)"
443
+ @update:model-value="(c) => updateSelected(c ? indexes[index] : null)"
467
444
  :disabled="options.disableSelection?.(recordsWithSummary[index]) === true"
468
445
  />
469
446
  </template>
@@ -0,0 +1,11 @@
1
+ import { type ComputedRef, computed } from 'vue'
2
+
3
+ export function smartComputed<T>(
4
+ getter: () => T,
5
+ comparator = (oldValue: T, newValue: T) => JSON.stringify(oldValue) === JSON.stringify(newValue)
6
+ ): ComputedRef<T> {
7
+ return computed((oldValue) => {
8
+ const newValue = getter()
9
+ return oldValue === undefined || !comparator(oldValue, newValue) ? newValue : oldValue
10
+ })
11
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
3
  "type": "module",
4
- "version": "4.14.1",
4
+ "version": "4.14.2",
5
5
  "packageManager": "pnpm@9.15.4",
6
6
  "description": "Vue Components for Global Brain Design System.",
7
7
  "author": "Kia Ishii <ka.ishii@globalbrains.com>",