@bagelink/vue 1.4.69 → 1.4.73
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/AddressSearch.vue.d.ts +29 -0
- package/dist/components/AddressSearch.vue.d.ts.map +1 -1
- package/dist/components/Btn.vue.d.ts +2 -0
- package/dist/components/Btn.vue.d.ts.map +1 -1
- package/dist/components/Dropdown.vue.d.ts +11 -0
- package/dist/components/Dropdown.vue.d.ts.map +1 -1
- package/dist/components/Icon/Icon.vue.d.ts +1 -0
- package/dist/components/Icon/Icon.vue.d.ts.map +1 -1
- package/dist/components/Modal.vue.d.ts.map +1 -1
- package/dist/components/dataTable/DataTable.vue.d.ts.map +1 -1
- package/dist/components/dataTable/useTableData.d.ts.map +1 -1
- package/dist/components/dataTable/useTableSelection.d.ts +1 -1
- package/dist/components/dataTable/useTableSelection.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts +58 -0
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/index.cjs +7 -7
- package/dist/index.mjs +5 -5
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/Btn.vue +42 -29
- package/src/components/Dropdown.vue +72 -31
- package/src/components/Icon/Icon.vue +29 -4
- package/src/components/Modal.vue +3 -0
- package/src/components/dataTable/DataTable.vue +20 -44
- package/src/components/dataTable/useTableData.ts +11 -3
- package/src/components/dataTable/useTableSelection.ts +40 -1
- package/src/styles/appearance.css +97 -0
package/package.json
CHANGED
package/src/components/Btn.vue
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import type { IconType, ThemeType } from '@bagelink/vue'
|
|
3
3
|
import type { SetupContext } from 'vue'
|
|
4
4
|
import { Icon, Loading } from '@bagelink/vue'
|
|
5
|
-
import { useSlots } from 'vue'
|
|
5
|
+
import { useSlots, ref, onMounted, onUnmounted } from 'vue'
|
|
6
6
|
import { RouterLink } from 'vue-router'
|
|
7
7
|
|
|
8
8
|
const props = withDefaults(
|
|
@@ -10,6 +10,8 @@ const props = withDefaults(
|
|
|
10
10
|
disabled?: boolean
|
|
11
11
|
icon?: IconType
|
|
12
12
|
iconEnd?: IconType
|
|
13
|
+
iconSize?: number | string
|
|
14
|
+
iconMobileSize?: number | string
|
|
13
15
|
color?: ThemeType
|
|
14
16
|
theme?: ThemeType
|
|
15
17
|
flat?: boolean
|
|
@@ -41,6 +43,26 @@ const props = withDefaults(
|
|
|
41
43
|
},
|
|
42
44
|
)
|
|
43
45
|
|
|
46
|
+
const isMobileScreen = ref(false)
|
|
47
|
+
|
|
48
|
+
const checkMobile = () => {
|
|
49
|
+
isMobileScreen.value = window.innerWidth <= 910
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
onMounted(() => {
|
|
53
|
+
checkMobile()
|
|
54
|
+
window.addEventListener('resize', checkMobile)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
onUnmounted(() => {
|
|
58
|
+
window.removeEventListener('resize', checkMobile)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const iconSizeComputed = $computed(() => {
|
|
62
|
+
if (props.iconSize !== undefined) return props.iconSize
|
|
63
|
+
return isMobileScreen.value ? 1 : 1.2
|
|
64
|
+
})
|
|
65
|
+
|
|
44
66
|
const isComponent = $computed(() => {
|
|
45
67
|
if (props.disabled) return props.is
|
|
46
68
|
if (props.to) return RouterLink
|
|
@@ -64,34 +86,25 @@ const slots: SetupContext['slots'] = useSlots()
|
|
|
64
86
|
</script>
|
|
65
87
|
|
|
66
88
|
<template>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
>
|
|
85
|
-
|
|
86
|
-
<div v-else class="bgl_btn-flex">
|
|
87
|
-
<Icon v-if="icon" :icon="icon" class="transition-400" />
|
|
88
|
-
<slot />
|
|
89
|
-
<template v-if="!slots.default && value">
|
|
90
|
-
{{ value }}
|
|
91
|
-
</template>
|
|
92
|
-
<Icon v-if="iconEnd" :icon="iconEnd" class="transition-400" />
|
|
93
|
-
</div>
|
|
94
|
-
</component>
|
|
89
|
+
<component :is="isComponent" v-ripple="ripple" v-bind="bind" :disabled="disabled" class="bgl_btn" :class="{
|
|
90
|
+
'bgl_btn-icon': icon && !slots.default && !value,
|
|
91
|
+
thin,
|
|
92
|
+
round,
|
|
93
|
+
'bgl_btn_flat': flat,
|
|
94
|
+
'bgl_btn-border': border || outline,
|
|
95
|
+
[`bgl_btn-${color}`]: color,
|
|
96
|
+
[`bgl_btn-${theme}`]: theme,
|
|
97
|
+
}" :tabindex="disabled ? -1 : 0" @click.stop="onClick" @keydown.enter="onClick" @keydown.space="onClick">
|
|
98
|
+
<Loading v-if="loading" class="h-100p" size="15" />
|
|
99
|
+
<div v-else class="bgl_btn-flex">
|
|
100
|
+
<Icon v-if="icon" :icon="icon" class="transition-400" :size="iconSizeComputed" :mobile-size="iconMobileSize" />
|
|
101
|
+
<slot />
|
|
102
|
+
<template v-if="!slots.default && value">
|
|
103
|
+
{{ value }}
|
|
104
|
+
</template>
|
|
105
|
+
<Icon v-if="iconEnd" :icon="iconEnd" class="transition-400" :size="iconSizeComputed" :mobile-size="iconMobileSize" />
|
|
106
|
+
</div>
|
|
107
|
+
</component>
|
|
95
108
|
</template>
|
|
96
109
|
|
|
97
110
|
<style scoped>
|
|
@@ -24,6 +24,12 @@ const {
|
|
|
24
24
|
noAutoFocus,
|
|
25
25
|
autoHide = true,
|
|
26
26
|
triggers = ['click'],
|
|
27
|
+
showTriggers,
|
|
28
|
+
hideTriggers,
|
|
29
|
+
popperTriggers = [],
|
|
30
|
+
popperShowTriggers = [],
|
|
31
|
+
popperHideTriggers = [],
|
|
32
|
+
delay,
|
|
27
33
|
|
|
28
34
|
} = defineProps<{
|
|
29
35
|
value?: string
|
|
@@ -42,6 +48,12 @@ const {
|
|
|
42
48
|
noAutoFocus?: boolean
|
|
43
49
|
autoHide?: boolean
|
|
44
50
|
triggers?: TriggerEvent[]
|
|
51
|
+
showTriggers?: TriggerEvent[]
|
|
52
|
+
hideTriggers?: TriggerEvent[]
|
|
53
|
+
popperTriggers?: TriggerEvent[]
|
|
54
|
+
popperShowTriggers?: TriggerEvent[]
|
|
55
|
+
popperHideTriggers?: TriggerEvent[]
|
|
56
|
+
delay?: number | { show: number, hide: number }
|
|
45
57
|
// theme
|
|
46
58
|
// referenceNode
|
|
47
59
|
// shown
|
|
@@ -95,45 +107,74 @@ const shouldDisablePositioning = $computed(() => {
|
|
|
95
107
|
return disablePlacement && isMobile
|
|
96
108
|
})
|
|
97
109
|
|
|
110
|
+
// Intelligent trigger configuration
|
|
111
|
+
const computedShowTriggers = $computed((): TriggerEvent[] => {
|
|
112
|
+
if (showTriggers !== undefined) return showTriggers
|
|
113
|
+
return triggers
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const computedHideTriggers = $computed((): TriggerEvent[] => {
|
|
117
|
+
if (hideTriggers !== undefined) return hideTriggers
|
|
118
|
+
// For click-only dropdowns, only hide on click
|
|
119
|
+
if (triggers.length === 1 && triggers[0] === 'click') {
|
|
120
|
+
return ['click'] as TriggerEvent[]
|
|
121
|
+
}
|
|
122
|
+
// For hover dropdowns, add click to hide triggers for better UX
|
|
123
|
+
if (triggers.includes('hover')) {
|
|
124
|
+
return [...triggers, 'click'] as TriggerEvent[]
|
|
125
|
+
}
|
|
126
|
+
return triggers
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
const computedPopperTriggers = $computed((): TriggerEvent[] => {
|
|
130
|
+
if (popperTriggers.length > 0) return popperTriggers
|
|
131
|
+
// For hover dropdowns, enable hover on popper to keep it open
|
|
132
|
+
if (triggers.includes('hover')) {
|
|
133
|
+
return ['hover'] as TriggerEvent[]
|
|
134
|
+
}
|
|
135
|
+
return []
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
const computedPopperHideTriggers = $computed((): TriggerEvent[] => {
|
|
139
|
+
if (popperHideTriggers && popperHideTriggers.length > 0) return popperHideTriggers
|
|
140
|
+
// For hover dropdowns, make sure popper hides on mouseout
|
|
141
|
+
if (triggers.includes('hover')) {
|
|
142
|
+
return ['hover'] as TriggerEvent[]
|
|
143
|
+
}
|
|
144
|
+
return []
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const computedAutoHide = $computed(() => {
|
|
148
|
+
// Both hover and click dropdowns should support autoHide for outside click behavior
|
|
149
|
+
return autoHide
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
const computedDelay = $computed((): number | { show: number, hide: number } | undefined => {
|
|
153
|
+
if (delay !== undefined) return delay
|
|
154
|
+
// For hover dropdowns, add a hide delay
|
|
155
|
+
if (triggers.includes('hover')) {
|
|
156
|
+
return { show: 0, hide: 50 } // 50ms delay before hiding
|
|
157
|
+
}
|
|
158
|
+
// For click dropdowns, no delay
|
|
159
|
+
return 0
|
|
160
|
+
})
|
|
161
|
+
|
|
98
162
|
defineExpose({ show, hide, shown })
|
|
99
163
|
</script>
|
|
100
164
|
|
|
101
165
|
<template>
|
|
102
166
|
<DDown
|
|
103
|
-
ref="ddownRef"
|
|
104
|
-
|
|
105
|
-
:
|
|
106
|
-
:
|
|
107
|
-
:positioning-disabled="shouldDisablePositioning"
|
|
108
|
-
:placement
|
|
109
|
-
:autoHide
|
|
110
|
-
:triggers
|
|
111
|
-
@hide="emit('hide')"
|
|
112
|
-
@show="emit('show')"
|
|
167
|
+
ref="ddownRef" v-model:shown="shown" :disabled :noAutoFocus :positioning-disabled="shouldDisablePositioning"
|
|
168
|
+
:placement :autoHide="computedAutoHide" :delay="computedDelay" :triggers :showTriggers="computedShowTriggers"
|
|
169
|
+
:hideTriggers="computedHideTriggers" :popperTriggers="computedPopperTriggers" :popperShowTriggers
|
|
170
|
+
:popperHideTriggers="computedPopperHideTriggers" @hide="emit('hide')" @show="emit('show')"
|
|
113
171
|
>
|
|
114
172
|
<div ref="triggerRef" />
|
|
115
|
-
<slot
|
|
116
|
-
|
|
117
|
-
:show
|
|
118
|
-
:hide
|
|
119
|
-
>
|
|
120
|
-
<Btn
|
|
121
|
-
:class="triggerClass"
|
|
122
|
-
:iconEnd
|
|
123
|
-
:icon
|
|
124
|
-
:value
|
|
125
|
-
:thin
|
|
126
|
-
:flat
|
|
127
|
-
:outline
|
|
128
|
-
:round
|
|
129
|
-
:color
|
|
130
|
-
/>
|
|
173
|
+
<slot name="trigger" :show :hide :shown>
|
|
174
|
+
<Btn :class="triggerClass" :iconEnd :icon :value :thin :flat :outline :round :color />
|
|
131
175
|
</slot>
|
|
132
|
-
<template #popper="{ hide, show }">
|
|
133
|
-
<slot
|
|
134
|
-
:hide
|
|
135
|
-
:show
|
|
136
|
-
/>
|
|
176
|
+
<template #popper="{ hide: hidePopper, show: showPopper, shown: popperShown }">
|
|
177
|
+
<slot :hide="hidePopper" :show="showPopper" :shown="popperShown" />
|
|
137
178
|
</template>
|
|
138
179
|
</DDown>
|
|
139
180
|
</template>
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { IconType } from '@bagelink/vue'
|
|
3
3
|
import { FONT_AWESOME_ICONS, MATERIAL_ICONS, FONT_AWESOME_BRANDS_ICONS } from './constants'
|
|
4
|
+
import { ref, onMounted, onUnmounted } from 'vue'
|
|
4
5
|
|
|
5
6
|
const props = withDefaults(defineProps<{
|
|
6
7
|
icon?: IconType
|
|
7
8
|
name?: IconType
|
|
8
9
|
size?: number | string
|
|
10
|
+
mobileSize?: number | string
|
|
9
11
|
color?: string
|
|
10
12
|
round?: boolean
|
|
11
13
|
weight?: number | string
|
|
@@ -17,6 +19,28 @@ const props = withDefaults(defineProps<{
|
|
|
17
19
|
|
|
18
20
|
const iconRender = $computed(() => props.icon || props.name) as IconType
|
|
19
21
|
|
|
22
|
+
const isMobile = ref(false)
|
|
23
|
+
|
|
24
|
+
const checkMobile = () => {
|
|
25
|
+
isMobile.value = window.innerWidth <= 910
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
onMounted(() => {
|
|
29
|
+
checkMobile()
|
|
30
|
+
window.addEventListener('resize', checkMobile)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
onUnmounted(() => {
|
|
34
|
+
window.removeEventListener('resize', checkMobile)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const computedSize = $computed(() => {
|
|
38
|
+
if (isMobile.value && props.mobileSize !== undefined) {
|
|
39
|
+
return props.mobileSize
|
|
40
|
+
}
|
|
41
|
+
return props.size
|
|
42
|
+
})
|
|
43
|
+
|
|
20
44
|
const iconRenderType = $computed(() => {
|
|
21
45
|
if (props.fontAwesome && (FONT_AWESOME_ICONS.includes(iconRender) || FONT_AWESOME_BRANDS_ICONS.includes(iconRender))) return 'font-awesome'
|
|
22
46
|
if (MATERIAL_ICONS.includes(iconRender)) return 'material'
|
|
@@ -32,11 +56,12 @@ const isFaBrand = $computed(() => FONT_AWESOME_BRANDS_ICONS.includes(iconRender)
|
|
|
32
56
|
v-if="iconRenderType === 'material'"
|
|
33
57
|
class="bgl_icon-font notranslate"
|
|
34
58
|
:class="{ 'round flex aspect-ratio-1 justify-content-center': round }"
|
|
35
|
-
:style="{
|
|
59
|
+
:style="{
|
|
60
|
+
'fontSize': `${computedSize}rem`,
|
|
36
61
|
color,
|
|
37
62
|
'font-variation-settings': `'FILL' ${fill ? 1 : 0}, 'wght' ${weight || 400}`,
|
|
38
|
-
|
|
39
|
-
|
|
63
|
+
'width': round ? `calc(${computedSize}rem * 2)` : 'auto',
|
|
64
|
+
'height': round ? `calc(${computedSize}rem * 2)` : 'auto',
|
|
40
65
|
}"
|
|
41
66
|
translate="no"
|
|
42
67
|
>
|
|
@@ -53,7 +78,7 @@ const isFaBrand = $computed(() => FONT_AWESOME_BRANDS_ICONS.includes(iconRender)
|
|
|
53
78
|
'far': !fill && !isFaBrand,
|
|
54
79
|
},
|
|
55
80
|
]"
|
|
56
|
-
:style="{ 'fontSize': `${
|
|
81
|
+
:style="{ 'fontSize': `${computedSize}rem`, color, 'font-variation-settings': `'wght' ${weight || 400}` }"
|
|
57
82
|
translate="no"
|
|
58
83
|
/>
|
|
59
84
|
</template>
|
package/src/components/Modal.vue
CHANGED
|
@@ -93,6 +93,7 @@ onUnmounted(() => {
|
|
|
93
93
|
flat
|
|
94
94
|
icon="close"
|
|
95
95
|
thin
|
|
96
|
+
icon-mobile-size="1.4"
|
|
96
97
|
@click="closeModal"
|
|
97
98
|
/>
|
|
98
99
|
<Title v-if="title" class="modal-title" tag="h3" :label="title" />
|
|
@@ -106,6 +107,7 @@ onUnmounted(() => {
|
|
|
106
107
|
thin
|
|
107
108
|
round
|
|
108
109
|
color="white"
|
|
110
|
+
icon-mobile-size="1.4"
|
|
109
111
|
@click="closeModal"
|
|
110
112
|
/>
|
|
111
113
|
</div>
|
|
@@ -134,6 +136,7 @@ onUnmounted(() => {
|
|
|
134
136
|
text-align: center;
|
|
135
137
|
font-weight: 600;
|
|
136
138
|
font-size: 20px;
|
|
139
|
+
margin-inline-end: 2rem;
|
|
137
140
|
margin-top: 0.5rem;
|
|
138
141
|
margin-bottom: 0 !important;
|
|
139
142
|
width: 100%;
|
|
@@ -121,76 +121,46 @@ const showLoading = computed(() => loading.value)
|
|
|
121
121
|
</script>
|
|
122
122
|
|
|
123
123
|
<template>
|
|
124
|
-
<div
|
|
125
|
-
class="table-list-wrap h-100"
|
|
126
|
-
v-bind="containerProps"
|
|
127
|
-
:class="{ overflowHiddenLoading: showLoading }"
|
|
128
|
-
>
|
|
124
|
+
<div class="table-list-wrap h-100" v-bind="containerProps" :class="{ overflowHiddenLoading: showLoading }">
|
|
129
125
|
<Loading v-if="showLoading" class="h100p" />
|
|
130
126
|
<div v-bind="wrapperProps" :class="{ 'pointer-events-none': showLoading }">
|
|
131
|
-
<table class="infinite-wrapper">
|
|
127
|
+
<table class="infinite-wrapper" :class="{ selecting: computedSelectedItems.length }">
|
|
132
128
|
<thead class="row first-row">
|
|
133
129
|
<th v-if="isSelectable">
|
|
134
|
-
<input
|
|
135
|
-
ref="allSelectorEl"
|
|
136
|
-
type="checkbox"
|
|
137
|
-
@click.stop
|
|
138
|
-
@change="(e) => toggleSelectAll(e)"
|
|
139
|
-
>
|
|
130
|
+
<input ref="allSelectorEl" type="checkbox" @click.stop @change="(e) => toggleSelectAll(e)">
|
|
140
131
|
<!-- @change="toggleSelectAll" -->
|
|
141
132
|
</th>
|
|
142
133
|
<th
|
|
143
|
-
v-for="field in computedSchema"
|
|
144
|
-
:key="field.id"
|
|
145
|
-
class="col"
|
|
134
|
+
v-for="field in computedSchema" :key="field.id" class="col"
|
|
146
135
|
@click="toggleSort(field?.id || '')"
|
|
147
136
|
>
|
|
148
137
|
<div class="flex">
|
|
149
138
|
{{ field.label || keyToLabel(field?.id) }}
|
|
150
|
-
<div
|
|
151
|
-
class="
|
|
152
|
-
:class="{ sorted: sortField === field?.id }"
|
|
153
|
-
>
|
|
154
|
-
<Icon
|
|
155
|
-
:class="{ desc: sortDirection === 'DESC' }"
|
|
156
|
-
icon="keyboard_arrow_up"
|
|
157
|
-
/>
|
|
139
|
+
<div class="list-arrows" :class="{ sorted: sortField === field?.id }">
|
|
140
|
+
<Icon :class="{ desc: sortDirection === 'DESC' }" icon="keyboard_arrow_up" />
|
|
158
141
|
</div>
|
|
159
142
|
</div>
|
|
160
143
|
</th>
|
|
161
144
|
</thead>
|
|
162
145
|
<tbody>
|
|
163
146
|
<tr
|
|
164
|
-
v-for="{ data: row } in list"
|
|
165
|
-
:key="row?.id || `row-${Math.random()}`"
|
|
147
|
+
v-for="{ data: row } in list" :key="row?.id || `row-${Math.random()}`"
|
|
166
148
|
class="row row-item position-relative"
|
|
167
149
|
:class="{ selected: row?.id && computedSelectedItems.includes(row.id) }"
|
|
168
|
-
@click="toggleSelectItem(row)"
|
|
150
|
+
@click="(event) => toggleSelectItem(row, event)"
|
|
169
151
|
>
|
|
170
152
|
<td v-if="isSelectable">
|
|
171
153
|
<div @click.stop>
|
|
172
|
-
<input
|
|
173
|
-
v-model="selectedItems"
|
|
174
|
-
type="checkbox"
|
|
175
|
-
:value="row?.id || ''"
|
|
176
|
-
>
|
|
154
|
+
<input v-model="selectedItems" type="checkbox" :value="row?.id || ''">
|
|
177
155
|
</div>
|
|
178
156
|
</td>
|
|
179
157
|
<td
|
|
180
|
-
v-for="field in computedSchema"
|
|
181
|
-
:key="`${field.id}-${row?.id || Math.random()}`"
|
|
158
|
+
v-for="field in computedSchema" :key="`${field.id}-${row?.id || Math.random()}`"
|
|
182
159
|
class="col"
|
|
183
160
|
>
|
|
184
|
-
<slot
|
|
185
|
-
v-if="field.id && slots?.[field.id]"
|
|
186
|
-
:name="field.id"
|
|
187
|
-
:row="row"
|
|
188
|
-
:field="field"
|
|
189
|
-
/>
|
|
161
|
+
<slot v-if="field.id && slots?.[field.id]" :name="field.id" :row="row" :field="field" />
|
|
190
162
|
<div v-else>
|
|
191
|
-
<component
|
|
192
|
-
:is="renderFieldForRow(field, row)"
|
|
193
|
-
/>
|
|
163
|
+
<component :is="renderFieldForRow(field, row)" />
|
|
194
164
|
</div>
|
|
195
165
|
</td>
|
|
196
166
|
</tr>
|
|
@@ -233,13 +203,19 @@ tbody tr.selected:hover {
|
|
|
233
203
|
border-radius: 5px;
|
|
234
204
|
object-fit: cover;
|
|
235
205
|
}
|
|
206
|
+
|
|
236
207
|
.col:has(img) {
|
|
237
208
|
padding-inline-end: 0.5rem;
|
|
238
209
|
}
|
|
239
210
|
|
|
240
|
-
.
|
|
211
|
+
.selecting .col {
|
|
212
|
+
user-select: none;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.overflowHiddenLoading {
|
|
241
216
|
overflow: hidden !important;
|
|
242
217
|
}
|
|
218
|
+
|
|
243
219
|
.list-arrows.sorted .desc {
|
|
244
220
|
transform: rotate(180deg);
|
|
245
221
|
display: inline-block;
|
|
@@ -313,7 +289,7 @@ th {
|
|
|
313
289
|
padding: 0rem 0.25rem;
|
|
314
290
|
}
|
|
315
291
|
|
|
316
|
-
.col
|
|
292
|
+
.col>div {
|
|
317
293
|
display: flex;
|
|
318
294
|
gap: 0.5rem;
|
|
319
295
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { BglFormSchemaT } from '@bagelink/vue'
|
|
1
|
+
import type { BglFormSchemaT, Field } from '@bagelink/vue'
|
|
2
|
+
import type { DefaultPathsOptions } from 'type-fest/source/paths'
|
|
2
3
|
import type { MaybeRefOrGetter } from 'vue'
|
|
3
4
|
import type { SortDirectionsT } from '../../types/TableSchema'
|
|
4
|
-
import { useBglSchema, isDate, keyToLabel } from '@bagelink/vue'
|
|
5
|
+
import { useBglSchema, isDate, keyToLabel, formatDate } from '@bagelink/vue'
|
|
5
6
|
import { computed, ref, watch, toValue } from 'vue'
|
|
6
7
|
|
|
7
8
|
const NON_DIGIT_REGEX = /[^\d.-]/g
|
|
@@ -30,6 +31,13 @@ interface TransformedDataBase {
|
|
|
30
31
|
|
|
31
32
|
type TransformedData<TransDataT> = TransDataT & TransformedDataBase
|
|
32
33
|
|
|
34
|
+
function autoTransform<T>(field: Field<T, DefaultPathsOptions>): Field<T, DefaultPathsOptions> {
|
|
35
|
+
if ((field.id === 'created_at' || field.id === 'updated_at') && !field.transform) {
|
|
36
|
+
field.transform = (val?: any) => val ? formatDate(val) : val
|
|
37
|
+
}
|
|
38
|
+
return field
|
|
39
|
+
}
|
|
40
|
+
|
|
33
41
|
export function useTableData<T extends { [key: string]: any }>(options: UseTableDataOptions<T>) {
|
|
34
42
|
// Sorting state
|
|
35
43
|
const sortField = ref('')
|
|
@@ -55,7 +63,7 @@ export function useTableData<T extends { [key: string]: any }>(options: UseTable
|
|
|
55
63
|
|
|
56
64
|
// If we have a valid schema with fields, filter out fields without an ID
|
|
57
65
|
if (Array.isArray(schema) && schema.length > 0) {
|
|
58
|
-
resolvedSchema.value = schema.filter(field => field && field.id)
|
|
66
|
+
resolvedSchema.value = schema.filter(field => field && field.id).map(autoTransform)
|
|
59
67
|
} else if (Array.isArray(dataValue) && dataValue.length > 0) {
|
|
60
68
|
// If no schema is provided or it's empty, generate a default schema from the data
|
|
61
69
|
const firstItem = dataValue[0]
|
|
@@ -9,6 +9,7 @@ export interface UseTableSelectionOptions<T> extends TableSelectionOptions<T> {
|
|
|
9
9
|
|
|
10
10
|
export function useTableSelection<T extends { [key: string]: any }>(options: UseTableSelectionOptions<T>) {
|
|
11
11
|
const allSelectorEl = ref<HTMLInputElement>()
|
|
12
|
+
const lastSelectedIndex = ref<number | null>(null)
|
|
12
13
|
const computedSelectedItems = computed(() => options.selectedItems.value)
|
|
13
14
|
const isSelectable = computed(() => options.selectable === true && Array.isArray(options.selectedItems.value))
|
|
14
15
|
const allItems = $computed(() => options.data.value)
|
|
@@ -39,7 +40,37 @@ export function useTableSelection<T extends { [key: string]: any }>(options: Use
|
|
|
39
40
|
= !allSelected && options.selectedItems.value.length > 0
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
function toggleSelectItem(item: T) {
|
|
43
|
+
function toggleSelectItem(item: T, event?: MouseEvent) {
|
|
44
|
+
const currentIndex = allItems.findIndex(i => i.id === item.id)
|
|
45
|
+
|
|
46
|
+
// Handle Shift+click for range selection
|
|
47
|
+
if (event?.shiftKey && lastSelectedIndex.value !== null && currentIndex !== -1) {
|
|
48
|
+
const startIndex = Math.min(lastSelectedIndex.value, currentIndex)
|
|
49
|
+
const endIndex = Math.max(lastSelectedIndex.value, currentIndex)
|
|
50
|
+
|
|
51
|
+
// Get all items in the range
|
|
52
|
+
const rangeItems = allItems.slice(startIndex, endIndex + 1)
|
|
53
|
+
const rangeItemIds = rangeItems.map(i => i.id)
|
|
54
|
+
|
|
55
|
+
// Check if all items in range are currently selected
|
|
56
|
+
const allRangeSelected = rangeItemIds.every(id => options.selectedItems.value.includes(id)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if (allRangeSelected) {
|
|
60
|
+
// Deselect all items in range
|
|
61
|
+
options.selectedItems.value = options.selectedItems.value.filter(id => !rangeItemIds.includes(id)
|
|
62
|
+
)
|
|
63
|
+
} else {
|
|
64
|
+
// Select all items in range (add missing ones)
|
|
65
|
+
const newSelections = rangeItemIds.filter(id => !options.selectedItems.value.includes(id)
|
|
66
|
+
)
|
|
67
|
+
options.selectedItems.value.push(...newSelections)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Regular single item selection logic
|
|
43
74
|
if (computedSelectedItems.value.length === 0) {
|
|
44
75
|
// Clean the item if a cleanData function is provided, otherwise use the default cleaning
|
|
45
76
|
const cleanItem = options.cleanData ? options.cleanData(item) : { ...item }
|
|
@@ -54,14 +85,19 @@ export function useTableSelection<T extends { [key: string]: any }>(options: Use
|
|
|
54
85
|
}
|
|
55
86
|
|
|
56
87
|
options.onSelect(cleanItem)
|
|
88
|
+
lastSelectedIndex.value = currentIndex
|
|
57
89
|
return
|
|
58
90
|
}
|
|
91
|
+
|
|
59
92
|
const index = computedSelectedItems.value.indexOf(item.id)
|
|
60
93
|
if (index > -1) {
|
|
61
94
|
options.selectedItems.value.splice(index, 1)
|
|
62
95
|
} else {
|
|
63
96
|
options.selectedItems.value.push(item.id)
|
|
64
97
|
}
|
|
98
|
+
|
|
99
|
+
// Update last selected index for future range selections
|
|
100
|
+
lastSelectedIndex.value = currentIndex
|
|
65
101
|
}
|
|
66
102
|
|
|
67
103
|
// Update toggleSelectAll to pass allItems to updateAllSelectorState
|
|
@@ -73,6 +109,9 @@ export function useTableSelection<T extends { [key: string]: any }>(options: Use
|
|
|
73
109
|
} else {
|
|
74
110
|
options.selectedItems.value = []
|
|
75
111
|
}
|
|
112
|
+
|
|
113
|
+
// Reset last selected index when selecting/deselecting all
|
|
114
|
+
lastSelectedIndex.value = null
|
|
76
115
|
}
|
|
77
116
|
|
|
78
117
|
return {
|
|
@@ -145,10 +145,18 @@
|
|
|
145
145
|
color: var(--bgl-primary-light) !important;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
.bg-blue {
|
|
149
|
+
background: var(--bgl-blue) !important;
|
|
150
|
+
}
|
|
151
|
+
|
|
148
152
|
.bg-blue-20 {
|
|
149
153
|
background: var(--bgl-blue-20) !important;
|
|
150
154
|
}
|
|
151
155
|
|
|
156
|
+
.color-blue {
|
|
157
|
+
color: var(--bgl-blue) !important;
|
|
158
|
+
}
|
|
159
|
+
|
|
152
160
|
.color-blue-20 {
|
|
153
161
|
color: var(--bgl-blue-20) !important;
|
|
154
162
|
}
|
|
@@ -830,6 +838,46 @@
|
|
|
830
838
|
scrollbar-gutter: stable both-edges;
|
|
831
839
|
}
|
|
832
840
|
|
|
841
|
+
.mix-blend-mode-multiply {
|
|
842
|
+
mix-blend-mode: multiply;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
.mix-blend-mode-screen {
|
|
846
|
+
mix-blend-mode: screen;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
.mix-blend-mode-overlay {
|
|
850
|
+
mix-blend-mode: overlay;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
.mix-blend-mode-darken {
|
|
854
|
+
mix-blend-mode: darken;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
.mix-blend-mode-lighten {
|
|
858
|
+
mix-blend-mode: lighten;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
.mix-blend-mode-color-dodge {
|
|
862
|
+
mix-blend-mode: color-dodge;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
.mix-blend-mode-color-burn {
|
|
866
|
+
mix-blend-mode: color-burn;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
.mix-blend-mode-hard-light {
|
|
870
|
+
mix-blend-mode: hard-light;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
.mix-blend-mode-soft-light {
|
|
874
|
+
mix-blend-mode: soft-light;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
.mix-blend-mode-none,
|
|
878
|
+
.mix-blend-mode-normal {
|
|
879
|
+
mix-blend-mode: normal;
|
|
880
|
+
}
|
|
833
881
|
|
|
834
882
|
@media screen and (max-width: 910px) {
|
|
835
883
|
.m_scrollbar-gutter {
|
|
@@ -987,10 +1035,18 @@
|
|
|
987
1035
|
color: var(--bgl-primary-light) !important;
|
|
988
1036
|
}
|
|
989
1037
|
|
|
1038
|
+
.m_bg-blue {
|
|
1039
|
+
background: var(--bgl-blue) !important;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
990
1042
|
.m_bg-blue-20 {
|
|
991
1043
|
background: var(--bgl-blue-20) !important;
|
|
992
1044
|
}
|
|
993
1045
|
|
|
1046
|
+
.m_color-blue {
|
|
1047
|
+
color: var(--bgl-blue) !important;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
994
1050
|
.m_color-blue-20 {
|
|
995
1051
|
color: var(--bgl-blue-20) !important;
|
|
996
1052
|
}
|
|
@@ -1641,4 +1697,45 @@
|
|
|
1641
1697
|
transform: rotateY(0deg) !important;
|
|
1642
1698
|
}
|
|
1643
1699
|
|
|
1700
|
+
.m_mix-blend-mode-multiply {
|
|
1701
|
+
mix-blend-mode: multiply;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
.m_mix-blend-mode-screen {
|
|
1705
|
+
mix-blend-mode: screen;
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
.m_mix-blend-mode-overlay {
|
|
1709
|
+
mix-blend-mode: overlay;
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
.m_mix-blend-mode-darken {
|
|
1713
|
+
mix-blend-mode: darken;
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
.m_mix-blend-mode-lighten {
|
|
1717
|
+
mix-blend-mode: lighten;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
.m_mix-blend-mode-color-dodge {
|
|
1721
|
+
mix-blend-mode: color-dodge;
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
.m_mix-blend-mode-color-burn {
|
|
1725
|
+
mix-blend-mode: color-burn;
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
.m_mix-blend-mode-hard-light {
|
|
1729
|
+
mix-blend-mode: hard-light;
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
.m_mix-blend-mode-soft-light {
|
|
1733
|
+
mix-blend-mode: soft-light;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
.m_mix-blend-mode-none,
|
|
1737
|
+
.m_mix-blend-mode-normal {
|
|
1738
|
+
mix-blend-mode: normal;
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1644
1741
|
}
|