@globalbrain/sefirot 4.37.1 → 4.38.1

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.
@@ -97,7 +97,7 @@ const props = withDefaults(defineProps<Props>(), {
97
97
  canSort: true
98
98
  })
99
99
 
100
- const selected = defineModel<number[]>('selected')
100
+ const selected = defineModel<any[]>('selected')
101
101
  const hideConditions = defineModel<boolean>('hideConditions', { default: false })
102
102
 
103
103
  const emit = defineEmits<{
@@ -320,7 +320,7 @@ function onViewUpdated(newSelect: string[], newSelectable: string[], overrides:
320
320
  emit('overrides-updated', _overrides.value)
321
321
  }
322
322
 
323
- function onUpdateSelected(value: number[]) {
323
+ function onUpdateSelected(value: any[]) {
324
324
  selected.value = value
325
325
  }
326
326
 
@@ -339,6 +339,16 @@ function onNext() {
339
339
  }
340
340
 
341
341
  defineExpose({
342
+ /**
343
+ * Retrieve the current records in the catalog. This method is required when
344
+ * the parent component needs to access the records directly, for example, to
345
+ * handle mapping for selected records. Like `updateRecords`, this is also a
346
+ * hacky method and should be avoided if possible.
347
+ */
348
+ records(): Record<string, any>[] {
349
+ return result.value?.data ?? []
350
+ },
351
+
342
352
  /**
343
353
  * Mutates the fetched records directly. This exposed method can be used to
344
354
  * do in-place updates of records from the parent component. However, it's
@@ -17,7 +17,7 @@ import { useTrans } from '../../../composables/Lang'
17
17
  export interface Props {
18
18
  queryPh?: string
19
19
  filterPresets?: FilterPresets[]
20
- selected?: number[]
20
+ selected?: any[]
21
21
  showQuery?: boolean
22
22
  showFilters?: boolean
23
23
  isConditionActive?: boolean
@@ -14,11 +14,12 @@ const props = defineProps<{
14
14
  result?: LensResult
15
15
  overrides?: Record<string, Partial<FieldData>>
16
16
  loading: boolean
17
- selected?: number[]
17
+ selected?: any[]
18
+ indexField?: string
18
19
  }>()
19
20
 
20
21
  const emit = defineEmits<{
21
- 'update:selected': [value: number[]]
22
+ 'update:selected': [value: any[]]
22
23
  'filter-updated': [filter: any[]]
23
24
  'sort-updated': [sort: LensQuerySort]
24
25
  'cell-clicked': [value: any, record: any]
@@ -87,10 +88,11 @@ const table = useTable({
87
88
  records,
88
89
  orders,
89
90
  columns,
91
+ indexField: props.indexField,
90
92
  borderless: true
91
93
  })
92
94
 
93
- function onSelect(value?: number[]) {
95
+ function onSelect(value?: any[]) {
94
96
  emit('update:selected', value ?? [])
95
97
  }
96
98
 
@@ -1,7 +1,7 @@
1
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
- import xor from 'lodash-es/xor'
4
+ import isEqual from 'lodash-es/isEqual'
5
5
  import { type CSSProperties, computed, nextTick, reactive, ref, toValue, unref, useTemplateRef, watch } from 'vue'
6
6
  import { type Table } from '../composables/Table'
7
7
  import { type VirtualRow, useTableAnimation } from '../composables/TableAnimation'
@@ -185,10 +185,9 @@ const control = computed({
185
185
  }
186
186
  })
187
187
 
188
- watch(indexes, (newValue, oldValue) => {
188
+ watch(indexes, (newValue) => {
189
189
  if (Array.isArray(selected.value)) {
190
- const removed = xor(newValue, oldValue)
191
- updateSelected(selected.value.filter((item) => !removed.includes(item)))
190
+ updateSelected(newValue.filter((item) => includesSelection(selected.value as any[], item)))
192
191
  }
193
192
  })
194
193
 
@@ -431,6 +430,10 @@ function getCell(key: string, index: number) {
431
430
  return isSummary(index) && col?.summaryCell ? col?.summaryCell : col?.cell
432
431
  }
433
432
 
433
+ function includesSelection(items: unknown[], target: unknown): boolean {
434
+ return items.some((item) => isEqual(item, target))
435
+ }
436
+
434
437
  function updateSelected(items: any) {
435
438
  if (Array.isArray(selected.value)) {
436
439
  selected.value = [...items] as any
@@ -440,11 +443,17 @@ function updateSelected(items: any) {
440
443
  }
441
444
 
442
445
  function addSelected(item: any) {
443
- updateSelected([...(selected.value as any), item])
446
+ const items = selected.value as any[]
447
+
448
+ if (includesSelection(items, item)) {
449
+ return
450
+ }
451
+
452
+ updateSelected([...items, item])
444
453
  }
445
454
 
446
455
  function removeSelected(item: any) {
447
- updateSelected((selected.value as any[]).filter((i) => i !== item))
456
+ updateSelected((selected.value as any[]).filter((i) => !isEqual(i, item)))
448
457
  }
449
458
 
450
459
  function getColWidth(key: string) {
@@ -603,13 +612,13 @@ function onResizeEnd(data: { columnName: string; finalWidth: string }) {
603
612
  <template v-if="key === '__select' && !isSummary(item.index)">
604
613
  <SInputCheckbox
605
614
  v-if="Array.isArray(selected)"
606
- :model-value="selected.includes(indexes[item.index])"
615
+ :model-value="includesSelection(selected, indexes[item.index])"
607
616
  :disabled="options.disableSelection?.(recordsWithSummary[item.index]) === true"
608
617
  @update:model-value="(c) => (c ? addSelected : removeSelected)(indexes[item.index])"
609
618
  />
610
619
  <SInputRadio
611
620
  v-else
612
- :model-value="selected === indexes[item.index]"
621
+ :model-value="isEqual(selected, indexes[item.index])"
613
622
  :disabled="options.disableSelection?.(recordsWithSummary[item.index]) === true"
614
623
  @update:model-value="(c) => updateSelected(c ? indexes[item.index] : null)"
615
624
  />
@@ -26,6 +26,10 @@ const props = defineProps<{
26
26
  const computedCell = computed<TableCell | undefined>(() =>
27
27
  typeof props.cell === 'function' ? props.cell(props.value, props.record) : props.cell
28
28
  )
29
+
30
+ const valueIsImagePath = computed(() => {
31
+ return typeof props.value === 'string' && props.value.includes('/')
32
+ })
29
33
  </script>
30
34
 
31
35
  <template>
@@ -89,8 +93,8 @@ const computedCell = computed<TableCell | undefined>(() =>
89
93
  v-else-if="computedCell.type === 'avatar'"
90
94
  :value
91
95
  :record
92
- :image="computedCell.image ?? ((value as string).includes('/') ? value : null)"
93
- :name="computedCell.name ?? ((value as string).includes('/') ? null : value)"
96
+ :image="computedCell.image ?? (valueIsImagePath ? value : null)"
97
+ :name="computedCell.name ?? (valueIsImagePath ? null : value)"
94
98
  :link="computedCell.link"
95
99
  :color="computedCell.color"
96
100
  :on-click="computedCell.onClick"
package/lib/http/Http.ts CHANGED
@@ -3,6 +3,7 @@ import { parse as parseCookie } from '@tinyhttp/cookie'
3
3
  import FileSaver from 'file-saver'
4
4
  import { FetchError, type FetchOptions, type FetchResponse } from 'ofetch'
5
5
  import { stringify } from 'qs'
6
+ import { objectToFormData } from '../support/Http'
6
7
 
7
8
  type Config = ReturnType<typeof import('../stores/HttpConfig').useHttpConfig>
8
9
 
@@ -87,7 +88,7 @@ export class Http {
87
88
  })
88
89
 
89
90
  if (hasFile) {
90
- const formData = this.objectToFormData(body, undefined, undefined, true)
91
+ const formData = objectToFormData(body, undefined, undefined, true)
91
92
  formData.append(this.config.payloadKey, payload)
92
93
  body = formData
93
94
  } else {
@@ -111,7 +112,7 @@ export class Http {
111
112
  }
112
113
 
113
114
  async upload<T = any>(url: string, body?: any, options?: FetchOptions): Promise<T> {
114
- return this.post<T>(url, this.objectToFormData(body), options)
115
+ return this.post<T>(url, objectToFormData(body), options)
115
116
  }
116
117
 
117
118
  async download(url: string, options?: FetchOptions): Promise<void> {
@@ -130,44 +131,6 @@ export class Http {
130
131
 
131
132
  FileSaver.saveAs(blob, filename as string)
132
133
  }
133
-
134
- private objectToFormData(
135
- obj: any,
136
- form?: FormData,
137
- namespace?: string,
138
- onlyFiles = false
139
- ): FormData {
140
- const fd = form || new FormData()
141
- let formKey: string
142
-
143
- Object.keys(obj).forEach((property) => {
144
- if (namespace) {
145
- formKey = `${namespace}[${property}]`
146
- } else {
147
- formKey = property
148
- }
149
-
150
- if (obj[property] === undefined) {
151
- return
152
- }
153
-
154
- if (
155
- typeof obj[property] === 'object'
156
- && !(obj[property] instanceof Blob)
157
- && obj[property] !== null
158
- ) {
159
- this.objectToFormData(obj[property], fd, property, onlyFiles)
160
- } else {
161
- const value = obj[property] === null ? '' : obj[property]
162
- if (onlyFiles && !(value instanceof Blob)) {
163
- return
164
- }
165
- fd.append(formKey, value)
166
- }
167
- })
168
-
169
- return fd
170
- }
171
134
  }
172
135
 
173
136
  export function isFetchError(e: unknown): e is FetchError {
@@ -0,0 +1,42 @@
1
+ export function objectToFormData(
2
+ obj: any,
3
+ form?: FormData,
4
+ namespace?: string,
5
+ onlyFiles = false
6
+ ): FormData {
7
+ const fd = form || new FormData()
8
+
9
+ if (obj == null) {
10
+ return fd
11
+ }
12
+
13
+ let formKey: string
14
+
15
+ Object.keys(obj).forEach((property) => {
16
+ if (namespace) {
17
+ formKey = `${namespace}[${property}]`
18
+ } else {
19
+ formKey = property
20
+ }
21
+
22
+ if (obj[property] === undefined) {
23
+ return
24
+ }
25
+
26
+ if (
27
+ typeof obj[property] === 'object'
28
+ && !(obj[property] instanceof Blob)
29
+ && obj[property] !== null
30
+ ) {
31
+ objectToFormData(obj[property], fd, formKey, onlyFiles)
32
+ } else {
33
+ const value = obj[property] === null ? '' : obj[property]
34
+ if (onlyFiles && !(value instanceof Blob)) {
35
+ return
36
+ }
37
+ fd.append(formKey, value)
38
+ }
39
+ })
40
+
41
+ return fd
42
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
- "version": "4.37.1",
3
+ "version": "4.38.1",
4
4
  "description": "Vue Components for Global Brain Design System.",
5
5
  "keywords": [
6
6
  "components",
@@ -53,14 +53,14 @@
53
53
  "coverage": "vitest run --coverage",
54
54
  "test": "pnpm run type && pnpm run lint && pnpm run coverage",
55
55
  "test:fail": "pnpm run type && pnpm run lint:fail && pnpm run coverage",
56
- "release": "release-it"
56
+ "release": "pnpm whoami >/dev/null 2>&1 || pnpm login && release-it"
57
57
  },
58
58
  "dependencies": {
59
59
  "@iconify-json/ph": "^1.2.2",
60
60
  "@iconify-json/ri": "^1.2.10",
61
61
  "@popperjs/core": "^2.11.8",
62
- "@sentry/browser": "^10.39.0",
63
- "@sentry/vue": "^10.39.0",
62
+ "@sentry/browser": "^10.40.0",
63
+ "@sentry/vue": "^10.40.0",
64
64
  "@tanstack/vue-virtual": "3.0.0-beta.62",
65
65
  "@tinyhttp/content-disposition": "^2.2.4",
66
66
  "@tinyhttp/cookie": "^2.1.1",
@@ -72,7 +72,7 @@
72
72
  "@types/markdown-it": "^14.1.2",
73
73
  "@types/qs": "^6.14.0",
74
74
  "@vitejs/plugin-vue": "^6.0.4",
75
- "@vue/reactivity": "^3.5.28",
75
+ "@vue/reactivity": "^3.5.29",
76
76
  "@vuelidate/core": "^2.0.3",
77
77
  "@vuelidate/validators": "^2.0.4",
78
78
  "@vueuse/core": "^14.2.1",
@@ -97,7 +97,7 @@
97
97
  "unplugin-icons": "^23.0.1",
98
98
  "v-calendar": "3.0.1",
99
99
  "vite": "^7.3.1",
100
- "vue": "^3.5.28",
100
+ "vue": "^3.5.29",
101
101
  "vue-draggable-plus": "^0.6.1",
102
102
  "vue-router": "^4.6.4"
103
103
  },
@@ -105,8 +105,8 @@
105
105
  "@globalbrain/eslint-config": "^3.0.1",
106
106
  "@histoire/plugin-vue": "1.0.0-beta.1",
107
107
  "@release-it/conventional-changelog": "^10.0.5",
108
- "@types/jsdom": "^27.0.0",
109
- "@types/node": "^25.3.0",
108
+ "@types/jsdom": "^28.0.0",
109
+ "@types/node": "^25.3.3",
110
110
  "@vitest/coverage-v8": "^4.0.18",
111
111
  "@vue/test-utils": "^2.4.6",
112
112
  "eslint": "^9.39.3",
@@ -118,5 +118,5 @@
118
118
  "vitest": "^4.0.18",
119
119
  "vue-tsc": "^3.2.5"
120
120
  },
121
- "packageManager": "pnpm@10.30.1"
121
+ "packageManager": "pnpm@10.30.3"
122
122
  }