@bagelink/vue 0.0.1139 → 0.0.1141

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 (37) hide show
  1. package/dist/components/Btn.vue.d.ts.map +1 -1
  2. package/dist/components/DataTable/DataTable.vue.d.ts +31 -0
  3. package/dist/components/DataTable/DataTable.vue.d.ts.map +1 -0
  4. package/dist/components/DataTable/tableTypes.d.ts +35 -0
  5. package/dist/components/DataTable/tableTypes.d.ts.map +1 -0
  6. package/dist/components/DataTable/useSorting.d.ts +7 -0
  7. package/dist/components/DataTable/useSorting.d.ts.map +1 -0
  8. package/dist/components/DataTable/useTableData.d.ts +13 -0
  9. package/dist/components/DataTable/useTableData.d.ts.map +1 -0
  10. package/dist/components/DataTable/useTableSelection.d.ts +10 -0
  11. package/dist/components/DataTable/useTableSelection.d.ts.map +1 -0
  12. package/dist/components/DataTable/useTableVirtualization.d.ts +25 -0
  13. package/dist/components/DataTable/useTableVirtualization.d.ts.map +1 -0
  14. package/dist/components/TableSchema.vue.d.ts +2 -2
  15. package/dist/components/form/BagelForm.vue.d.ts.map +1 -1
  16. package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
  17. package/dist/components/index.d.ts +2 -1
  18. package/dist/components/index.d.ts.map +1 -1
  19. package/dist/index.cjs +3414 -3328
  20. package/dist/index.mjs +3415 -3329
  21. package/dist/style.css +272 -272
  22. package/dist/types/TableSchema.d.ts +36 -0
  23. package/dist/types/TableSchema.d.ts.map +1 -0
  24. package/package.json +1 -1
  25. package/src/components/Btn.vue +2 -2
  26. package/src/components/{TableSchema.vue → DataTable/DataTable.vue} +62 -154
  27. package/src/components/DataTable/tableTypes.d.ts +0 -0
  28. package/src/components/DataTable/tableTypes.ts +38 -0
  29. package/src/components/DataTable/useSorting.ts +30 -0
  30. package/src/components/DataTable/useTableData.ts +95 -0
  31. package/src/components/DataTable/useTableSelection.ts +45 -0
  32. package/src/components/DataTable/useTableVirtualization.ts +33 -0
  33. package/src/components/Icon/Icon.vue +1 -1
  34. package/src/components/form/BagelForm.vue +5 -12
  35. package/src/components/form/inputs/RichText/index.vue +6 -4
  36. package/src/components/index.ts +3 -2
  37. package/src/types/TableSchema.ts +39 -0
@@ -0,0 +1,36 @@
1
+ import { BglFormSchemaT } from '..';
2
+ import { Ref, ComputedRef } from 'vue';
3
+ export type SortDirectionsT = 'ASC' | 'DESC';
4
+ export type EmitOrderT = `${string} ${SortDirectionsT}`;
5
+ export interface TableSchemaProps<T extends Record<string, any> = Record<string, any>> {
6
+ data: T[];
7
+ schema?: BglFormSchemaT<T> | (() => BglFormSchemaT<T>);
8
+ showFields?: string[];
9
+ useServerSort?: boolean;
10
+ selectable?: boolean;
11
+ onLastItemVisible?: () => void;
12
+ }
13
+ export interface SortingOptions<T> {
14
+ onSort: (field: string, direction: SortDirectionsT) => void;
15
+ }
16
+ export interface TableSelectionOptions<T> {
17
+ selectable: boolean | undefined;
18
+ selectedItems: {
19
+ value: string[];
20
+ };
21
+ onSelect: (item: T) => void;
22
+ }
23
+ export interface TableVirtualizationOptions<T> {
24
+ data: Ref<T[]> | ComputedRef<T[]>;
25
+ itemHeight: number;
26
+ onLastItemVisible?: () => void;
27
+ }
28
+ export interface TableDataOptions<T> {
29
+ data: Ref<T[]> | ComputedRef<T[]>;
30
+ schema?: BglFormSchemaT<T> | (() => BglFormSchemaT<T>);
31
+ showFields?: string[];
32
+ sortField: string;
33
+ sortDirection: SortDirectionsT;
34
+ useServerSort?: boolean;
35
+ }
36
+ //# sourceMappingURL=TableSchema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TableSchema.d.ts","sourceRoot":"","sources":["../../src/types/TableSchema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,KAAK,CAAA;AAE3C,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,MAAM,CAAA;AAC5C,MAAM,MAAM,UAAU,GAAG,GAAG,MAAM,IAAI,eAAe,EAAE,CAAA;AAEvD,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IACpF,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,MAAM,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,CAAC,CAAC,CAAC,CAAA;IACtD,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAA;CAC9B;AAED,MAAM,WAAW,cAAc,CAAC,CAAC;IAChC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,KAAK,IAAI,CAAA;CAC3D;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC;IACvC,UAAU,EAAE,OAAO,GAAG,SAAS,CAAA;IAC/B,aAAa,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;IAClC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAA;CAC3B;AAED,MAAM,WAAW,0BAA0B,CAAC,CAAC;IAC5C,IAAI,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,CAAA;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAA;CAC9B;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC;IAClC,IAAI,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,CAAA;IACjC,MAAM,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,CAAC,CAAC,CAAC,CAAA;IACtD,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,eAAe,CAAA;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAA;CACvB"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/vue",
3
3
  "type": "module",
4
- "version": "0.0.1139",
4
+ "version": "0.0.1141",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Neveh Allon",
@@ -81,12 +81,12 @@ const slots: SetupContext['slots'] = useSlots()
81
81
  >
82
82
  <Loading v-if="loading" class="h-100p" size="15" />
83
83
  <div v-else class="bgl_btn-flex">
84
- <Icon v-if="icon" :icon="icon" />
84
+ <Icon v-if="icon" :icon="icon" class="transition-400" />
85
85
  <slot />
86
86
  <template v-if="!slots.default && value">
87
87
  {{ value }}
88
88
  </template>
89
- <Icon v-if="iconEnd" :icon="iconEnd" />
89
+ <Icon v-if="iconEnd" :icon="iconEnd" class="transition-400" />
90
90
  </div>
91
91
  </component>
92
92
  </template>
@@ -1,37 +1,20 @@
1
1
  <script setup lang="ts" generic="T extends Record<string, any>">
2
- import type { BglFormSchemaT } from '@bagelink/vue'
2
+ import type { TableSchemaProps } from '../../types/TableSchema'
3
3
  import {
4
4
  BglComponent,
5
5
  Icon,
6
- isDate,
7
6
  keyToLabel,
8
- useBglSchema
9
7
  } from '@bagelink/vue'
10
- import {
11
- useVirtualList,
12
- useIntersectionObserver,
13
- until
14
- } from '@vueuse/core'
15
- import { computed, useSlots, watch } from 'vue'
8
+
9
+ import { useSlots, watch, computed } from 'vue'
10
+ import { useTableData } from './useTableData'
11
+ import { useTableSelection } from './useTableSelection'
12
+ import { useTableVirtualization } from './useTableVirtualization'
16
13
 
17
14
  export type SortDirectionsT = 'ASC' | 'DESC'
18
15
  export type EmitOrderT = `${string} ${SortDirectionsT}`
19
16
 
20
- const {
21
- data,
22
- schema,
23
- showFields,
24
- onLastItemVisible,
25
- useServerSort = false,
26
- selectable = false,
27
- } = defineProps<{
28
- data: T[]
29
- schema?: BglFormSchemaT<T> | (() => BglFormSchemaT<T>)
30
- showFields?: string[]
31
- useServerSort?: boolean
32
- selectable?: boolean
33
- onLastItemVisible?: () => void
34
- }>()
17
+ const props = defineProps<TableSchemaProps<T>>()
35
18
 
36
19
  const emit = defineEmits<{
37
20
  'update:selectedItems': [string[]]
@@ -40,8 +23,6 @@ const emit = defineEmits<{
40
23
  'lastItemVisible': []
41
24
  }>()
42
25
 
43
- const NON_DIGIT_REGEX = /[^\d.-]/g
44
-
45
26
  const slots: any = useSlots()
46
27
 
47
28
  const loading = defineModel('loading', { default: false })
@@ -54,148 +35,75 @@ const selectedItems = defineModel<string[]>(
54
35
  }
55
36
  )
56
37
 
57
- let sortField = $ref('')
58
- let sortDirection = $ref<SortDirectionsT>('ASC')
59
-
60
- const allSelectorEl = $ref<HTMLInputElement | undefined>()
61
- const lastItemEl = $ref<HTMLTableRowElement | null>()
62
-
63
- const computedSelectedItems = $computed(() => selectedItems.value)
64
- const computedItemHeight = $computed(() => `${itemHeight.value}px`)
65
- const isSelectable = $computed(() => selectable === true && Array.isArray(selectedItems.value))
66
- const computedSortField = $computed(() => `_transformed_${sortField}`)
67
- const computedSchema = $computed(() => useBglSchema<T>({
68
- schema,
69
- showFields,
70
- data,
71
- }))
72
-
73
- const computedData = computed(() => {
74
- if (!sortField || useServerSort === true) return data.map(transform)
75
-
76
- return (
77
- data
78
- .map(transform)
79
- .sort(
80
- (a, z) => {
81
- let aValue = a[computedSortField] ?? a[sortField] ?? ''
82
- let bValue = z[computedSortField] ?? z[sortField] ?? ''
83
-
84
- if (isDate(aValue) && isDate(bValue)) {
85
- // ? now we can sort by as number
86
- aValue = new Date(aValue).getTime()
87
- bValue = new Date(bValue).getTime()
88
- }
89
-
90
- const numAValue = Number.parseInt(`${aValue}`.replaceAll(NON_DIGIT_REGEX, ''), 10)
91
- const numBValue = Number.parseInt(`${bValue}`.replaceAll(NON_DIGIT_REGEX, ''), 10)
92
-
93
- if (!Number.isNaN(numAValue) && !Number.isNaN(numBValue)) {
94
- if (sortDirection === 'ASC') return numAValue - numBValue
95
- return numBValue - numAValue
96
- }
97
-
98
- if (typeof aValue === 'string') {
99
- if (sortDirection === 'ASC') return aValue.localeCompare(bValue)
100
- return bValue.localeCompare(aValue)
101
- }
102
-
103
- if (sortDirection === 'ASC') return aValue < bValue ? -1 : 1
104
- return aValue < bValue ? 1 : -1
105
- }
106
- )
107
- )
108
- })
109
-
110
- function transform(rowData: any) {
111
- const obj: { [key: string]: any } = { ...rowData }
112
- const schemaFields = computedSchema.filter((f: any) => f.id)
113
-
114
- for (const field of schemaFields) {
115
- const fieldData = rowData[`${field.id}`]
116
- const newFieldVal = field.transform?.(fieldData, rowData)
38
+ const data = computed(() => props.data)
117
39
 
118
- Object.assign(obj, {
119
- [`${field.id}`]: fieldData,
120
- [`_transformed_${field.id}`]: newFieldVal,
121
- })
122
- }
123
- return obj
124
- }
125
-
126
- function sort(fieldname: string) {
127
- if (sortField === fieldname) {
128
- if (sortDirection === 'ASC') sortDirection = 'DESC'
129
- else sortField = ''
130
- } else {
131
- sortField = fieldname
132
- sortDirection = 'ASC'
133
- }
134
- emit('orderBy', `${fieldname} ${sortDirection}`.trim() as EmitOrderT)
135
- }
136
-
137
- const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
40
+ const {
41
+ computedSchema,
138
42
  computedData,
139
- {
140
- itemHeight: itemHeight.value,
141
- overscan: 10,
43
+ sortField,
44
+ sortDirection,
45
+ toggleSort
46
+ } = useTableData<T>({
47
+ data,
48
+ schema: props.schema,
49
+ showFields: props.showFields,
50
+ useServerSort: props.useServerSort,
51
+ onSort: (field, direction) => {
52
+ emit('orderBy', `${field} ${direction}`.trim() as EmitOrderT)
142
53
  }
143
- )
54
+ })
144
55
 
145
- function updateAllSelectorState() {
146
- if (!allSelectorEl) return
147
- const allSelected = computedData.value.length === computedSelectedItems.length
148
- && computedData.value.every(s => s.id && computedSelectedItems.includes(s.id))
149
- allSelectorEl.checked = allSelected
150
- allSelectorEl.indeterminate = !allSelected && computedSelectedItems.length > 0
151
- }
56
+ const {
57
+ computedSelectedItems,
58
+ isSelectable,
59
+ allSelectorEl,
60
+ updateAllSelectorState,
61
+ toggleSelectItem,
62
+ toggleSelectAll
63
+ } = useTableSelection<T>({
64
+ selectable: props.selectable,
65
+ selectedItems,
66
+ onSelect: (item) => { emit('select', item) }
67
+ })
152
68
 
153
- function toggleSelectItem(item: { [key: string]: any }) {
154
- if (computedSelectedItems.length === 0) {
155
- const obj = { ...item }
156
- Object.keys(obj).forEach(key => key.startsWith('_transformed_') && delete obj[key])
157
- emit('select', obj as T)
158
- return
69
+ const {
70
+ list,
71
+ containerProps,
72
+ wrapperProps,
73
+ scrollTo,
74
+ lastItemEl,
75
+ registerLastItemObserver
76
+ } = useTableVirtualization<T>({
77
+ data: computedData,
78
+ itemHeight: itemHeight.value,
79
+ onLastItemVisible: () => {
80
+ emit('lastItemVisible')
81
+ props.onLastItemVisible?.()
159
82
  }
160
- const index = computedSelectedItems.indexOf(item.id)
161
- index > -1 ? computedSelectedItems.splice(index, 1) : computedSelectedItems.push(item.id)
162
- }
163
-
164
- function toggleSelectAll(event: Event) {
165
- const value = (event.target as HTMLInputElement).checked
166
- selectedItems.value = value ? computedData.value.map((d: any) => d.id) : []
167
- }
168
-
169
- async function registerLastItemObserver() {
170
- await until(() => lastItemEl).toBeTruthy()
83
+ })
171
84
 
172
- useIntersectionObserver(lastItemEl, ([entry]) => {
173
- if (entry.isIntersecting && computedData.value.length) {
174
- emit('lastItemVisible')
175
- }
176
- })
177
- }
85
+ const computedItemHeight = $computed(() => `${itemHeight.value}px`)
178
86
 
179
87
  watch(
180
- () => computedData.value.length,
181
- (newLength, oldLength) => {
182
- if (newLength === oldLength || onLastItemVisible !== undefined) return
183
- scrollTo(0)
184
- }
88
+ () => loading.value,
89
+ (newLoadingVal, oldLoadingVal) => {
90
+ if (newLoadingVal === oldLoadingVal) return
91
+ if (!newLoadingVal) registerLastItemObserver()
92
+ },
93
+ { immediate: true }
185
94
  )
186
95
 
187
96
  watch(
188
- () => computedSelectedItems.length,
97
+ computedSelectedItems,
189
98
  () => { updateAllSelectorState() }
190
99
  )
191
100
 
192
101
  watch(
193
- () => loading.value,
194
- (newLoadingVal, oldLoadingVal) => {
195
- if (newLoadingVal === oldLoadingVal) return
196
- if (!newLoadingVal) registerLastItemObserver()
197
- },
198
- { immediate: true }
102
+ computedData,
103
+ (newData, oldData) => {
104
+ if (newData.length === oldData.length || props.onLastItemVisible !== undefined) return
105
+ scrollTo(0)
106
+ }
199
107
  )
200
108
  </script>
201
109
 
@@ -226,7 +134,7 @@ watch(
226
134
  v-for="field in computedSchema"
227
135
  :key="field.id"
228
136
  class="col"
229
- @click="sort(field?.id || '')"
137
+ @click="toggleSort(field?.id || '')"
230
138
  >
231
139
  <div class="flex">
232
140
  {{ field.label || keyToLabel(field.id) }}
@@ -281,7 +189,7 @@ watch(
281
189
  </div>
282
190
  </td>
283
191
  </tr>
284
- <tr v-if="onLastItemVisible !== undefined" ref="lastItemEl" style="height: 1px;" />
192
+ <tr v-if="props.onLastItemVisible !== undefined" ref="lastItemEl" style="height: 1px;" />
285
193
  </tbody>
286
194
  </table>
287
195
  </div>
File without changes
@@ -0,0 +1,38 @@
1
+ import type { BglFormSchemaT } from '@bagelink/vue'
2
+
3
+ export type SortDirectionsT = 'ASC' | 'DESC'
4
+ export type EmitOrderT = `${string} ${SortDirectionsT}`
5
+
6
+ export interface TableSchemaProps<T extends Record<string, any> = Record<string, any>> {
7
+ data: T[]
8
+ schema?: BglFormSchemaT<T> | (() => BglFormSchemaT<T>)
9
+ showFields?: string[]
10
+ useServerSort?: boolean
11
+ selectable?: boolean
12
+ onLastItemVisible?: () => void
13
+ }
14
+
15
+ export interface SortingOptions<T> {
16
+ onSort: (field: string, direction: SortDirectionsT) => void
17
+ }
18
+
19
+ export interface TableSelectionOptions<T> {
20
+ selectable: boolean | undefined
21
+ selectedItems: { value: string[] }
22
+ onSelect: (item: T) => void
23
+ }
24
+
25
+ export interface TableVirtualizationOptions<T> {
26
+ data: T[]
27
+ itemHeight: number
28
+ onLastItemVisible?: () => void
29
+ }
30
+
31
+ export interface TableDataOptions<T> {
32
+ data: T[]
33
+ schema?: BglFormSchemaT<T> | (() => BglFormSchemaT<T>)
34
+ showFields?: string[]
35
+ sortField: string
36
+ sortDirection: SortDirectionsT
37
+ useServerSort?: boolean
38
+ }
@@ -0,0 +1,30 @@
1
+ import type { SortDirectionsT, SortingOptions } from '../../types/TableSchema'
2
+ import { ref, computed } from 'vue'
3
+
4
+ export function useSorting<T>(options: SortingOptions<T>) {
5
+ const sortFieldRef = ref('')
6
+ const sortDirectionRef = ref<SortDirectionsT>('ASC')
7
+
8
+ const sortField = computed(() => sortFieldRef.value)
9
+ const sortDirection = computed(() => sortDirectionRef.value)
10
+
11
+ function sort(fieldname: string) {
12
+ if (sortFieldRef.value === fieldname) {
13
+ if (sortDirectionRef.value === 'ASC') {
14
+ sortDirectionRef.value = 'DESC'
15
+ } else {
16
+ sortFieldRef.value = ''
17
+ }
18
+ } else {
19
+ sortFieldRef.value = fieldname
20
+ sortDirectionRef.value = 'ASC'
21
+ }
22
+ options.onSort(sortFieldRef.value, sortDirectionRef.value)
23
+ }
24
+
25
+ return {
26
+ sortField,
27
+ sortDirection,
28
+ sort
29
+ }
30
+ }
@@ -0,0 +1,95 @@
1
+ import type { TableDataOptions, SortDirectionsT } from '../../types/TableSchema'
2
+ import { useBglSchema, isDate } from '@bagelink/vue'
3
+ import { computed, ref } from 'vue'
4
+
5
+ const NON_DIGIT_REGEX = /[^\d.-]/g
6
+
7
+ export interface UseTableDataOptions<T> extends Omit<TableDataOptions<T>, 'sortField' | 'sortDirection'> {
8
+ onSort?: (field: string, direction: SortDirectionsT) => void
9
+ }
10
+
11
+ export function useTableData<T extends Record<string, any>>(options: UseTableDataOptions<T>) {
12
+ // Create reactive reference to data prop
13
+
14
+ // Sorting state
15
+ const sortField = ref('')
16
+ const sortDirection = ref<SortDirectionsT>('ASC')
17
+
18
+ const computedSchema = computed(() => useBglSchema<T>({
19
+ schema: options.schema,
20
+ showFields: options.showFields,
21
+ data: options.data.value,
22
+ }))
23
+
24
+ function transform(rowData: T): T {
25
+ const transformed = { ...rowData }
26
+ const schemaFields = computedSchema.value.filter((f: any) => f.id)
27
+
28
+ for (const field of schemaFields) {
29
+ const fieldData = rowData[`${field.id}`]
30
+ const newFieldVal = field.transform?.(fieldData, rowData)
31
+ ;(transformed as any)[`_transformed_${field.id}`] = newFieldVal
32
+ }
33
+ return transformed
34
+ }
35
+
36
+ const computedSortField = computed(() => `_transformed_${sortField.value}`)
37
+
38
+ const computedData = computed(() => {
39
+ const currentData = options.data.value
40
+ if (!sortField.value || options.useServerSort === true) {
41
+ return currentData.map(transform)
42
+ }
43
+
44
+ return currentData
45
+ .map(transform)
46
+ .sort((a, z) => {
47
+ let aValue = (a as any)[computedSortField.value] ?? a[sortField.value] ?? ''
48
+ let bValue = (z as any)[computedSortField.value] ?? z[sortField.value] ?? ''
49
+
50
+ if (isDate(aValue) && isDate(bValue)) {
51
+ aValue = new Date(aValue).getTime()
52
+ bValue = new Date(bValue).getTime()
53
+ }
54
+
55
+ const numAValue = Number.parseInt(`${aValue}`.replaceAll(NON_DIGIT_REGEX, ''), 10)
56
+ const numBValue = Number.parseInt(`${bValue}`.replaceAll(NON_DIGIT_REGEX, ''), 10)
57
+
58
+ if (!Number.isNaN(numAValue) && !Number.isNaN(numBValue)) {
59
+ if (sortDirection.value === 'ASC') return numAValue - numBValue
60
+ return numBValue - numAValue
61
+ }
62
+
63
+ if (typeof aValue === 'string') {
64
+ if (sortDirection.value === 'ASC') return aValue.localeCompare(bValue)
65
+ return bValue.localeCompare(aValue)
66
+ }
67
+
68
+ if (sortDirection.value === 'ASC') return aValue < bValue ? -1 : 1
69
+ return aValue < bValue ? 1 : -1
70
+ })
71
+ })
72
+
73
+ function toggleSort(fieldname: string) {
74
+ if (sortField.value === fieldname) {
75
+ if (sortDirection.value === 'ASC') {
76
+ sortDirection.value = 'DESC'
77
+ } else {
78
+ sortField.value = ''
79
+ }
80
+ } else {
81
+ sortField.value = fieldname
82
+ sortDirection.value = 'ASC'
83
+ }
84
+ options.onSort?.(sortField.value, sortDirection.value)
85
+ }
86
+
87
+ return {
88
+ computedSchema,
89
+ computedData,
90
+ transform,
91
+ sortField: computed(() => sortField.value),
92
+ sortDirection: computed(() => sortDirection.value),
93
+ toggleSort
94
+ }
95
+ }
@@ -0,0 +1,45 @@
1
+ import type { TableSelectionOptions } from '../../types/TableSchema'
2
+ import { computed, ref } from 'vue'
3
+
4
+ export function useTableSelection<T extends Record<string, any>>(options: TableSelectionOptions<T>) {
5
+ const allSelectorEl = ref<HTMLInputElement>()
6
+ const computedSelectedItems = computed(() => options.selectedItems.value)
7
+ const isSelectable = computed(() => options.selectable === true && Array.isArray(options.selectedItems.value))
8
+
9
+ function updateAllSelectorState() {
10
+ if (!allSelectorEl.value) return
11
+ const allSelected = computedSelectedItems.value.length === options.selectedItems.value.length
12
+ && computedSelectedItems.value.every(id => options.selectedItems.value.includes(id))
13
+ allSelectorEl.value.checked = allSelected
14
+ allSelectorEl.value.indeterminate = !allSelected && computedSelectedItems.value.length > 0
15
+ }
16
+
17
+ function toggleSelectItem(item: T) {
18
+ if (computedSelectedItems.value.length === 0) {
19
+ const cleanItem = { ...item }
20
+ Object.keys(cleanItem).forEach(key => key.startsWith('_transformed_') && delete cleanItem[key])
21
+ options.onSelect(cleanItem)
22
+ return
23
+ }
24
+ const index = computedSelectedItems.value.indexOf(item.id)
25
+ if (index > -1) {
26
+ options.selectedItems.value.splice(index, 1)
27
+ } else {
28
+ options.selectedItems.value.push(item.id)
29
+ }
30
+ }
31
+
32
+ function toggleSelectAll(event: Event) {
33
+ const value = (event.target as HTMLInputElement).checked
34
+ options.selectedItems.value = value ? computedSelectedItems.value.map((d: any) => d.id) : []
35
+ }
36
+
37
+ return {
38
+ computedSelectedItems,
39
+ isSelectable,
40
+ allSelectorEl,
41
+ updateAllSelectorState,
42
+ toggleSelectItem,
43
+ toggleSelectAll
44
+ }
45
+ }
@@ -0,0 +1,33 @@
1
+ import type { TableVirtualizationOptions } from '../../types/TableSchema'
2
+ import { useVirtualList, useIntersectionObserver, until } from '@vueuse/core'
3
+ import { ref } from 'vue'
4
+
5
+ export function useTableVirtualization<T>(options: TableVirtualizationOptions<T>) {
6
+ const lastItemEl = ref<HTMLTableRowElement | null>(null)
7
+ const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
8
+ options.data,
9
+ {
10
+ itemHeight: options.itemHeight,
11
+ overscan: 10,
12
+ }
13
+ )
14
+
15
+ async function registerLastItemObserver() {
16
+ await until(() => lastItemEl.value).toBeTruthy()
17
+
18
+ useIntersectionObserver(lastItemEl, ([entry]) => {
19
+ if (entry.isIntersecting && options.data.value.length) {
20
+ options.onLastItemVisible?.()
21
+ }
22
+ })
23
+ }
24
+
25
+ return {
26
+ list,
27
+ containerProps,
28
+ wrapperProps,
29
+ scrollTo,
30
+ lastItemEl,
31
+ registerLastItemObserver
32
+ }
33
+ }
@@ -10,7 +10,7 @@ const props = defineProps<{
10
10
  weight?: number | string
11
11
  }>()
12
12
 
13
- const iconRender = (props.icon || props.name) as IconType
13
+ const iconRender = $computed(() => props.icon || props.name) as IconType
14
14
 
15
15
  const iconRenderType = $computed(() => {
16
16
  if (MATERIAL_ICONS.includes(iconRender)) return 'material'
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { BglFormSchemaT, BglFormSchemaFnT, Field } from '@bagelink/vue'
3
3
  import type { VNode } from 'vue'
4
- import { BglForm, CheckInput, DateInput, FieldArray, FileUpload, NumberInput, RichText, SelectInput, TabsNav, TextInput, ToggleInput, UploadInput } from '@bagelink/vue'
4
+ import { BglForm, CheckInput, DateInput, FieldArray, FileUpload, NumberInput, RichText, SelectInput, TabsNav, TextInput, ToggleInput, UploadInput } from '@bagelink/vue'
5
5
  import { h, watch } from 'vue'
6
6
 
7
7
  interface Props {
@@ -63,17 +63,10 @@ function getComponent(field: Field) {
63
63
  return componentMap[field.$el as keyof typeof componentMap] ?? field.$el ?? 'div'
64
64
  }
65
65
 
66
- function processFieldValue(fieldType: any, value: any): any {
67
- if (value == null) return value
68
- if (fieldType === 'select' && typeof value === 'object') return value.value
69
- if (typeof value === 'object' && 'value' in value) return value.value
70
- return value
71
- }
72
-
73
- function updateFormData(fieldId: string, value: any, fieldType?: any) {
66
+ function updateFormData(fieldId: string, value: any) {
74
67
  formData = {
75
68
  ...formData,
76
- [fieldId]: processFieldValue(fieldType, value)
69
+ [fieldId]: value
77
70
  }
78
71
  emit('update:modelValue', formData)
79
72
  }
@@ -116,10 +109,10 @@ function renderSchemaField(field: Field): VNode | null {
116
109
  label,
117
110
  placeholder,
118
111
  disabled,
119
- 'modelValue': processFieldValue(field.$el, currentValue),
112
+ 'modelValue': currentValue,
120
113
  'onUpdate:modelValue': (value: any) => {
121
114
  if (!field.id) return
122
- updateFormData(field.id, value, field.$el)
115
+ updateFormData(field.id, value)
123
116
  field.onUpdate?.(value, formData)
124
117
  }
125
118
  }
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { ToolbarConfig } from './richTextTypes'
3
3
  import { CodeEditor, copyText, Btn } from '@bagelink/vue'
4
- import { watch, computed } from 'vue'
4
+ import { watch } from 'vue'
5
5
  import EditorToolbar from './components/EditorToolbar.vue'
6
6
  import { useCommands } from './composables/useCommands'
7
7
  import { useEditor } from './composables/useEditor'
@@ -15,8 +15,8 @@ const editor = useEditor()
15
15
  const commands = useCommands(editor.state, props.debug ? editor.debug : undefined)
16
16
 
17
17
  // Expose debug methods if debug mode is enabled
18
- const debugMethods = computed(() => props.debug ? editor.debug : undefined)
19
-
18
+ const debugMethods = $computed(() => props.debug ? editor.debug : undefined)
19
+ const hasRTL = $computed(() => /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/.test(props.modelValue))
20
20
  async function initEditor() {
21
21
  if (!iframe) {
22
22
  setTimeout(initEditor, 100)
@@ -30,7 +30,6 @@ async function initEditor() {
30
30
  doc.body.contentEditable = 'true'
31
31
 
32
32
  // Set default direction based on content
33
- const hasRTL = /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/.test(props.modelValue)
34
33
  doc.body.dir = hasRTL ? 'rtl' : 'ltr'
35
34
 
36
35
  const style = doc.createElement('style')
@@ -79,6 +78,9 @@ watch(() => props.modelValue, (newValue) => {
79
78
  })
80
79
 
81
80
  watch(() => editor.state.content, (newValue) => {
81
+ if (editor.state.doc?.body.innerHTML) {
82
+ editor.state.doc.body.dir = hasRTL ? 'rtl' : 'ltr'
83
+ }
82
84
  emit('update:modelValue', newValue)
83
85
  })
84
86
  </script>