@bagelink/vue 0.0.1145 → 0.0.1151
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/dist/components/DataPreview.vue.d.ts +12 -35
- package/dist/components/DataPreview.vue.d.ts.map +1 -1
- package/dist/components/DataTable/DataTable.vue.d.ts +1 -1
- package/dist/components/DataTable/DataTable.vue.d.ts.map +1 -1
- package/dist/components/DataTable/useTableData.d.ts +10 -2
- package/dist/components/DataTable/useTableData.d.ts.map +1 -1
- package/dist/components/Draggable/Draggable.vue.d.ts +45 -0
- package/dist/components/Draggable/Draggable.vue.d.ts.map +1 -0
- package/dist/components/Draggable/index.d.ts +5 -0
- package/dist/components/Draggable/index.d.ts.map +1 -0
- package/dist/components/Draggable/useDraggable.d.ts +31 -0
- package/dist/components/Draggable/useDraggable.d.ts.map +1 -0
- package/dist/components/Draggable/vDraggable.d.ts +4 -0
- package/dist/components/Draggable/vDraggable.d.ts.map +1 -0
- package/dist/components/ListView.vue.d.ts.map +1 -1
- package/dist/components/form/BagelForm.vue.d.ts.map +1 -1
- package/dist/components/form/FieldArray.vue.d.ts.map +1 -1
- package/dist/components/form/index.d.ts +0 -1
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/inputs/FileUpload.vue.d.ts +3 -0
- package/dist/components/form/inputs/FileUpload.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/NumberInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/Upload/UploadInput.vue.d.ts +3 -0
- package/dist/components/form/inputs/Upload/UploadInput.vue.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/composables/useSchemaField.d.ts +15 -0
- package/dist/composables/useSchemaField.d.ts.map +1 -0
- package/dist/index.cjs +1256 -802
- package/dist/index.mjs +1258 -804
- package/dist/style.css +272 -285
- package/package.json +1 -1
- package/src/components/DataPreview.vue +45 -116
- package/src/components/DataTable/DataTable.vue +18 -12
- package/src/components/DataTable/useTableData.ts +50 -16
- package/src/components/Draggable/Draggable.vue +64 -0
- package/src/components/Draggable/index.ts +4 -0
- package/src/components/Draggable/useDraggable.ts +632 -0
- package/src/components/Draggable/vDraggable.ts +17 -0
- package/src/components/ListView.vue +6 -2
- package/src/components/Pill.vue +1 -1
- package/src/components/form/BagelForm.vue +16 -101
- package/src/components/form/FieldArray.vue +45 -17
- package/src/components/form/index.ts +0 -1
- package/src/components/form/inputs/FileUpload.vue +10 -6
- package/src/components/form/inputs/NumberInput.vue +3 -1
- package/src/components/form/inputs/RichText/index.vue +1 -1
- package/src/components/form/inputs/Upload/UploadInput.vue +12 -7
- package/src/components/index.ts +5 -1
- package/src/composables/useSchemaField.ts +193 -0
- package/src/styles/text.css +15 -11
- package/src/components/form/BglField.vue +0 -132
- package/src/components/form/BglForm.vue +0 -157
package/package.json
CHANGED
|
@@ -1,133 +1,62 @@
|
|
|
1
|
-
<script lang="ts"
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}>(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (isDate(dataValue)) {
|
|
31
|
-
const date = new Date(dataValue)
|
|
32
|
-
return date.toDateString()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return dataValue
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function isUnset(value: any) {
|
|
39
|
-
if (!props.excludeUnset) return false
|
|
40
|
-
return (
|
|
41
|
-
value === undefined || value === null || value === '' || value.length === 0
|
|
42
|
-
)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const computedSchema = $computed(() => getFallbackSchema([itemData.value], props.showFields))
|
|
1
|
+
<script setup lang="ts" generic="T extends Record<string, any>">
|
|
2
|
+
import type { BaseBagelField } from '@bagelink/vue'
|
|
3
|
+
import type { Slots } from 'vue'
|
|
4
|
+
import type { TableSchemaProps } from '../types/TableSchema'
|
|
5
|
+
import { computed, useSlots } from 'vue'
|
|
6
|
+
import { useSchemaField } from '../composables/useSchemaField'
|
|
7
|
+
import { useTableData } from './DataTable/useTableData'
|
|
8
|
+
|
|
9
|
+
const props = defineProps<TableSchemaProps<T>>()
|
|
10
|
+
const slots = useSlots() as Slots & Record<string, (props: { row: T, field: BaseBagelField<T> }) => any>
|
|
11
|
+
|
|
12
|
+
const data = computed(() => Array.isArray(props.data) ? props.data : [props.data])
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
computedSchema,
|
|
16
|
+
computedData,
|
|
17
|
+
} = useTableData<T>({
|
|
18
|
+
data,
|
|
19
|
+
schema: props.schema,
|
|
20
|
+
showFields: props.showFields,
|
|
21
|
+
useServerSort: false
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const firstItem = computed(() => computedData.value[0] || {} as T)
|
|
25
|
+
|
|
26
|
+
const { renderField } = useSchemaField<T>({
|
|
27
|
+
mode: 'preview',
|
|
28
|
+
getRowData: () => firstItem.value
|
|
29
|
+
})
|
|
46
30
|
</script>
|
|
47
31
|
|
|
48
32
|
<template>
|
|
49
|
-
<div
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
<div v-if="iffer(field, itemData)" class="data-row m_py-05">
|
|
53
|
-
<div class="key">
|
|
54
|
-
<p class="m-0">
|
|
55
|
-
{{ field?.label || keyToLabel(field.id) }}
|
|
56
|
-
</p>
|
|
57
|
-
</div>
|
|
58
|
-
<BglField
|
|
59
|
-
v-model="itemData"
|
|
60
|
-
label=""
|
|
61
|
-
style="width: min-content; min-width: 140px"
|
|
62
|
-
:field
|
|
63
|
-
/>
|
|
64
|
-
</div>
|
|
33
|
+
<div class="data-preview">
|
|
34
|
+
<template v-for="field in computedSchema" :key="field.id">
|
|
35
|
+
<component :is="renderField(field, slots)" />
|
|
65
36
|
</template>
|
|
66
|
-
<div v-if="!schema?.length">
|
|
67
|
-
<template v-for="{ id, label } in computedSchema" :key="id">
|
|
68
|
-
<div v-if="!isUnset(itemData[id as any])" class="data-row">
|
|
69
|
-
<div class="key">
|
|
70
|
-
<p class="m-0">
|
|
71
|
-
{{ label }}
|
|
72
|
-
</p>
|
|
73
|
-
</div>
|
|
74
|
-
<div>
|
|
75
|
-
<p v-if="id" class="m-0">
|
|
76
|
-
{{ dataTransform(itemData[id]) }}
|
|
77
|
-
</p>
|
|
78
|
-
</div>
|
|
79
|
-
</div>
|
|
80
|
-
</template>
|
|
81
|
-
</div>
|
|
82
|
-
<slot name="footer" />
|
|
83
37
|
</div>
|
|
84
38
|
</template>
|
|
85
39
|
|
|
86
40
|
<style scoped>
|
|
87
|
-
data {
|
|
88
|
-
font-size: var(--input-font-size);
|
|
89
|
-
border: 1.1px solid var(--border-color);
|
|
90
|
-
padding: 20px;
|
|
91
|
-
border-radius: var(--input-border-radius);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
.data-row {
|
|
41
|
+
.data-preview {
|
|
95
42
|
display: flex;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
align-items: center;
|
|
99
|
-
min-height: 50px;
|
|
100
|
-
}
|
|
101
|
-
.data-row .bagel-input.shrink,
|
|
102
|
-
.data-row .bagel-input.shrink input {
|
|
103
|
-
min-width: 140px !important;
|
|
104
|
-
/* margin-top: -0.45rem !important; */
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
gap: 0.5rem;
|
|
105
45
|
}
|
|
106
46
|
|
|
107
|
-
.
|
|
108
|
-
display:
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
line-height: 1.3;
|
|
112
|
-
color: var(--input-color);
|
|
113
|
-
}
|
|
114
|
-
@media screen and (max-width: 910px) {
|
|
115
|
-
.data-row {
|
|
116
|
-
display: block;
|
|
117
|
-
/* min-height: 80px; */
|
|
118
|
-
padding-bottom: 1rem;
|
|
119
|
-
}
|
|
47
|
+
.preview-field {
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
gap: 0.5rem;
|
|
120
51
|
}
|
|
121
|
-
</style>
|
|
122
52
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
height: calc(var(--input-height) - 0.25rem) !important;
|
|
53
|
+
.field-label {
|
|
54
|
+
font-size: 0.8rem;
|
|
55
|
+
color: var(--bgl-black-tint);
|
|
56
|
+
font-weight: 500;
|
|
128
57
|
}
|
|
129
58
|
|
|
130
|
-
.
|
|
131
|
-
|
|
59
|
+
.field-value {
|
|
60
|
+
font-size: 0.95rem;
|
|
132
61
|
}
|
|
133
62
|
</style>
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts" generic="T extends Record<string, any>">
|
|
2
|
+
import type { BaseBagelField } from '@bagelink/vue'
|
|
3
|
+
import type { Slots } from 'vue'
|
|
2
4
|
import type { TableSchemaProps } from '../../types/TableSchema'
|
|
3
5
|
import {
|
|
4
|
-
BglComponent,
|
|
5
6
|
Icon,
|
|
6
7
|
keyToLabel,
|
|
7
8
|
} from '@bagelink/vue'
|
|
8
|
-
|
|
9
|
-
import {
|
|
9
|
+
import { useSlots, watch, computed, ref } from 'vue'
|
|
10
|
+
import { useSchemaField } from '../../composables/useSchemaField'
|
|
10
11
|
import { useTableData } from './useTableData'
|
|
11
12
|
import { useTableSelection } from './useTableSelection'
|
|
12
13
|
import { useTableVirtualization } from './useTableVirtualization'
|
|
@@ -23,7 +24,7 @@ const emit = defineEmits<{
|
|
|
23
24
|
'lastItemVisible': []
|
|
24
25
|
}>()
|
|
25
26
|
|
|
26
|
-
const slots
|
|
27
|
+
const slots = useSlots() as Slots & Record<string, (props: { row: T, field: BaseBagelField<T> }) => any>
|
|
27
28
|
|
|
28
29
|
const loading = defineModel('loading', { default: false })
|
|
29
30
|
const itemHeight = defineModel('itemHeight', { default: 50 })
|
|
@@ -36,6 +37,7 @@ const selectedItems = defineModel<string[]>(
|
|
|
36
37
|
)
|
|
37
38
|
|
|
38
39
|
const data = computed(() => props.data)
|
|
40
|
+
const currentRowId = ref<string>('')
|
|
39
41
|
|
|
40
42
|
const {
|
|
41
43
|
computedSchema,
|
|
@@ -82,6 +84,14 @@ const {
|
|
|
82
84
|
}
|
|
83
85
|
})
|
|
84
86
|
|
|
87
|
+
function renderFieldForRow(field: BaseBagelField<T>, row: T) {
|
|
88
|
+
const { renderField } = useSchemaField<T>({
|
|
89
|
+
mode: 'table',
|
|
90
|
+
getRowData: () => row
|
|
91
|
+
})
|
|
92
|
+
return renderField(field, slots)
|
|
93
|
+
}
|
|
94
|
+
|
|
85
95
|
const computedItemHeight = $computed(() => `${itemHeight.value}px`)
|
|
86
96
|
|
|
87
97
|
watch(
|
|
@@ -175,16 +185,12 @@ watch(
|
|
|
175
185
|
<slot
|
|
176
186
|
v-if="field.id && slots[field.id]"
|
|
177
187
|
:name="field.id"
|
|
178
|
-
:row
|
|
179
|
-
:field
|
|
188
|
+
:row="row"
|
|
189
|
+
:field="field"
|
|
180
190
|
/>
|
|
181
191
|
<div v-else>
|
|
182
|
-
<
|
|
183
|
-
:
|
|
184
|
-
class="embedded-field"
|
|
185
|
-
:field
|
|
186
|
-
:modelValue="row"
|
|
187
|
-
label=""
|
|
192
|
+
<component
|
|
193
|
+
:is="renderFieldForRow(field, row)"
|
|
188
194
|
/>
|
|
189
195
|
</div>
|
|
190
196
|
</td>
|
|
@@ -4,10 +4,25 @@ import { computed, ref } from 'vue'
|
|
|
4
4
|
|
|
5
5
|
const NON_DIGIT_REGEX = /[^\d.-]/g
|
|
6
6
|
|
|
7
|
+
// Components that should receive their value as a child/slot instead of a prop
|
|
8
|
+
const SLOT_VALUE_COMPONENTS = new Set(['div', 'span', 'p'])
|
|
9
|
+
|
|
10
|
+
// Components that should receive their value as src attribute
|
|
11
|
+
const SRC_VALUE_COMPONENTS = new Set(['img', 'iframe'])
|
|
12
|
+
|
|
7
13
|
export interface UseTableDataOptions<T> extends Omit<TableDataOptions<T>, 'sortField' | 'sortDirection'> {
|
|
8
14
|
onSort?: (field: string, direction: SortDirectionsT) => void
|
|
9
15
|
}
|
|
10
16
|
|
|
17
|
+
interface TransformedDataBase {
|
|
18
|
+
[key: `_transformed_${string}`]: any
|
|
19
|
+
[key: `_slot_${string}`]: boolean
|
|
20
|
+
[key: `_src_${string}`]: boolean
|
|
21
|
+
[key: `_original_${string}`]: any
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type TransformedData<T> = T & TransformedDataBase
|
|
25
|
+
|
|
11
26
|
export function useTableData<T extends Record<string, any>>(options: UseTableDataOptions<T>) {
|
|
12
27
|
// Create reactive reference to data prop
|
|
13
28
|
|
|
@@ -21,19 +36,40 @@ export function useTableData<T extends Record<string, any>>(options: UseTableDat
|
|
|
21
36
|
data: options.data.value,
|
|
22
37
|
}))
|
|
23
38
|
|
|
24
|
-
function transform(rowData: T): T {
|
|
25
|
-
const transformed = { ...rowData }
|
|
39
|
+
function transform(rowData: T): TransformedData<T> {
|
|
40
|
+
const transformed = { ...rowData } as TransformedData<T>
|
|
26
41
|
const schemaFields = computedSchema.value.filter((f: any) => f.id)
|
|
27
42
|
|
|
28
43
|
for (const field of schemaFields) {
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
44
|
+
const fieldId = field.id as keyof T
|
|
45
|
+
const fieldData = rowData[fieldId]
|
|
46
|
+
const transformKey = `_transformed_${String(fieldId)}` as keyof TransformedDataBase
|
|
47
|
+
const slotKey = `_slot_${String(fieldId)}` as keyof TransformedDataBase
|
|
48
|
+
const srcKey = `_src_${String(fieldId)}` as keyof TransformedDataBase
|
|
49
|
+
const originalKey = `_original_${String(fieldId)}` as keyof TransformedDataBase
|
|
50
|
+
|
|
51
|
+
// Store the original value
|
|
52
|
+
;(transformed as TransformedDataBase)[originalKey] = fieldData
|
|
53
|
+
|
|
54
|
+
// Determine if this component should receive value as slot or src
|
|
55
|
+
const isSlotValueComponent = typeof field.$el === 'string' && SLOT_VALUE_COMPONENTS.has(field.$el)
|
|
56
|
+
const isSrcValueComponent = typeof field.$el === 'string' && SRC_VALUE_COMPONENTS.has(field.$el)
|
|
57
|
+
|
|
58
|
+
;(transformed as TransformedDataBase)[slotKey] = isSlotValueComponent
|
|
59
|
+
;(transformed as TransformedDataBase)[srcKey] = isSrcValueComponent
|
|
60
|
+
|
|
61
|
+
if (field.transform) {
|
|
62
|
+
const newFieldVal = field.transform(fieldData, rowData)
|
|
63
|
+
// Store transformed value in _transformed_ key but keep original in the main field
|
|
64
|
+
;(transformed as TransformedDataBase)[transformKey] = newFieldVal
|
|
65
|
+
} else {
|
|
66
|
+
;(transformed as TransformedDataBase)[transformKey] = fieldData
|
|
67
|
+
}
|
|
32
68
|
}
|
|
33
69
|
return transformed
|
|
34
70
|
}
|
|
35
71
|
|
|
36
|
-
const computedSortField = computed(() => `_transformed_${sortField.value}`)
|
|
72
|
+
const computedSortField = computed(() => sortField.value ? `_transformed_${sortField.value}` : '')
|
|
37
73
|
|
|
38
74
|
const computedData = computed(() => {
|
|
39
75
|
const currentData = options.data.value
|
|
@@ -44,29 +80,27 @@ export function useTableData<T extends Record<string, any>>(options: UseTableDat
|
|
|
44
80
|
return currentData
|
|
45
81
|
.map(transform)
|
|
46
82
|
.sort((a, z) => {
|
|
47
|
-
|
|
48
|
-
|
|
83
|
+
const aValue = (a as any)[computedSortField.value] ?? ''
|
|
84
|
+
const bValue = (z as any)[computedSortField.value] ?? ''
|
|
49
85
|
|
|
50
86
|
if (isDate(aValue) && isDate(bValue)) {
|
|
51
|
-
|
|
52
|
-
|
|
87
|
+
return sortDirection.value === 'ASC'
|
|
88
|
+
? new Date(aValue).getTime() - new Date(bValue).getTime()
|
|
89
|
+
: new Date(bValue).getTime() - new Date(aValue).getTime()
|
|
53
90
|
}
|
|
54
91
|
|
|
55
92
|
const numAValue = Number.parseInt(`${aValue}`.replaceAll(NON_DIGIT_REGEX, ''), 10)
|
|
56
93
|
const numBValue = Number.parseInt(`${bValue}`.replaceAll(NON_DIGIT_REGEX, ''), 10)
|
|
57
94
|
|
|
58
95
|
if (!Number.isNaN(numAValue) && !Number.isNaN(numBValue)) {
|
|
59
|
-
|
|
60
|
-
return numBValue - numAValue
|
|
96
|
+
return sortDirection.value === 'ASC' ? numAValue - numBValue : numBValue - numAValue
|
|
61
97
|
}
|
|
62
98
|
|
|
63
99
|
if (typeof aValue === 'string') {
|
|
64
|
-
|
|
65
|
-
return bValue.localeCompare(aValue)
|
|
100
|
+
return sortDirection.value === 'ASC' ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue)
|
|
66
101
|
}
|
|
67
102
|
|
|
68
|
-
|
|
69
|
-
return aValue < bValue ? 1 : -1
|
|
103
|
+
return sortDirection.value === 'ASC' ? (aValue < bValue ? -1 : 1) : (aValue < bValue ? 1 : -1)
|
|
70
104
|
})
|
|
71
105
|
})
|
|
72
106
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ref, watch, onMounted, nextTick } from 'vue'
|
|
3
|
+
import type { DraggableEvent } from './useDraggable'
|
|
4
|
+
import { useDraggable } from './useDraggable'
|
|
5
|
+
|
|
6
|
+
const props = withDefaults(defineProps<{
|
|
7
|
+
modelValue?: any[]
|
|
8
|
+
group?: string
|
|
9
|
+
handle?: string
|
|
10
|
+
mode?: 'ghost' | 'line'
|
|
11
|
+
disabled?: boolean
|
|
12
|
+
}>(), {
|
|
13
|
+
mode: 'line',
|
|
14
|
+
disabled: false
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const emit = defineEmits<{
|
|
18
|
+
'update:modelValue': [value: any[]]
|
|
19
|
+
'start': [event: MouseEvent]
|
|
20
|
+
'end': [event: DraggableEvent]
|
|
21
|
+
}>()
|
|
22
|
+
|
|
23
|
+
const dragContainer = ref<HTMLElement>()
|
|
24
|
+
|
|
25
|
+
const { initDraggableContainer } = useDraggable({
|
|
26
|
+
group: props.group,
|
|
27
|
+
handle: props.handle,
|
|
28
|
+
mode: props.mode,
|
|
29
|
+
disabled: props.disabled,
|
|
30
|
+
items: props.modelValue,
|
|
31
|
+
onStart: (event) => { emit('start', event) },
|
|
32
|
+
onEnd: (event: DraggableEvent) => {
|
|
33
|
+
const newList = [...props.modelValue!]
|
|
34
|
+
const [removed] = newList.splice(event.oldIndex, 1)
|
|
35
|
+
newList.splice(event.newIndex, 0, removed)
|
|
36
|
+
emit('update:modelValue', newList)
|
|
37
|
+
|
|
38
|
+
emit('end', event)
|
|
39
|
+
nextTick(() => {
|
|
40
|
+
if (dragContainer.value) {
|
|
41
|
+
initDraggableContainer(dragContainer.value)
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
watch(() => props.modelValue, () => {
|
|
48
|
+
if (dragContainer.value) {
|
|
49
|
+
initDraggableContainer(dragContainer.value)
|
|
50
|
+
}
|
|
51
|
+
}, { deep: true })
|
|
52
|
+
|
|
53
|
+
onMounted(() => {
|
|
54
|
+
if (dragContainer.value) {
|
|
55
|
+
initDraggableContainer(dragContainer.value)
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<template>
|
|
61
|
+
<div ref="dragContainer">
|
|
62
|
+
<slot v-for="(item, index) in modelValue" :key="index" :item="item" :index="index" />
|
|
63
|
+
</div>
|
|
64
|
+
</template>
|