@bagelink/vue 0.0.1004 → 0.0.1008
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/BglVideo.vue.d.ts +8 -1
- package/dist/components/BglVideo.vue.d.ts.map +1 -1
- package/dist/components/Image.vue.d.ts +5 -0
- package/dist/components/Image.vue.d.ts.map +1 -1
- package/dist/components/Modal.vue.d.ts.map +1 -1
- package/dist/components/ModalForm.vue.d.ts +2 -2
- package/dist/components/ModalForm.vue.d.ts.map +1 -1
- package/dist/components/TableSchema.vue.d.ts.map +1 -1
- package/dist/components/form/BglField.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/composables/useEditor.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/media.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts +23 -35
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/layout/SidebarMenu.vue.d.ts.map +1 -1
- package/dist/components/layout/TabsNav.vue.d.ts.map +1 -1
- package/dist/index.cjs +196 -124
- package/dist/index.mjs +196 -124
- package/dist/style.css +766 -169
- package/dist/utils/timeAgo.d.ts.map +1 -1
- package/package.json +1 -2
- package/src/components/BglVideo.vue +28 -3
- package/src/components/Btn.vue +2 -2
- package/src/components/Image.vue +11 -6
- package/src/components/Modal.vue +3 -1
- package/src/components/ModalForm.vue +2 -2
- package/src/components/Pill.vue +2 -2
- package/src/components/TableSchema.vue +60 -71
- package/src/components/form/BglField.vue +2 -1
- package/src/components/form/inputs/RichText/composables/useEditor.ts +0 -1
- package/src/components/form/inputs/RichText/utils/media.ts +27 -5
- package/src/components/form/inputs/SelectInput.vue +9 -4
- package/src/components/layout/SidebarMenu.vue +3 -5
- package/src/components/layout/Tabs.vue +2 -2
- package/src/components/layout/TabsNav.vue +4 -2
- package/src/styles/layout.css +350 -0
- package/src/styles/mobilLayout.css +347 -32
- package/src/utils/timeAgo.ts +13 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeAgo.d.ts","sourceRoot":"","sources":["../../src/utils/timeAgo.ts"],"names":[],"mappings":"AAAA,KAAK,sBAAsB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"timeAgo.d.ts","sourceRoot":"","sources":["../../src/utils/timeAgo.ts"],"names":[],"mappings":"AAAA,KAAK,sBAAsB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAsDvD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,GAAE,sBAA6B,UAmC/E"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bagelink/vue",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.1008",
|
|
5
5
|
"description": "Bagel core sdk packages",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Neveh Allon",
|
|
@@ -80,7 +80,6 @@
|
|
|
80
80
|
"access": "public"
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
|
-
"heic2any": "^0.0.4",
|
|
84
83
|
"@vueuse/core": "^12.0.0",
|
|
85
84
|
"axios": "^1.7.9",
|
|
86
85
|
"floating-vue": "^5.2.2",
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { watch } from 'vue'
|
|
3
|
+
|
|
2
4
|
interface Props {
|
|
3
5
|
src?: string
|
|
4
6
|
autoplay?: boolean
|
|
@@ -6,14 +8,35 @@ interface Props {
|
|
|
6
8
|
aspectRatio?: string
|
|
7
9
|
controls?: boolean
|
|
8
10
|
loop?: boolean
|
|
11
|
+
status?: string
|
|
12
|
+
playsinline?: boolean
|
|
9
13
|
}
|
|
10
14
|
|
|
11
15
|
const props = defineProps<Props>()
|
|
12
16
|
|
|
17
|
+
const videoFormat = $computed(() => props.src?.split('.').pop()?.split('?').shift()?.toLowerCase() || 'mp4')
|
|
18
|
+
|
|
13
19
|
const aspectRatio = $computed(
|
|
14
20
|
() => props.aspectRatio?.replace(':', '/') || '16/9',
|
|
15
21
|
)
|
|
16
22
|
|
|
23
|
+
const video = $ref<HTMLVideoElement | null>()
|
|
24
|
+
|
|
25
|
+
function play() {
|
|
26
|
+
video?.play()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function pause() {
|
|
30
|
+
video?.pause()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
watch(() => props.status, (status) => {
|
|
34
|
+
if (status === 'play') play()
|
|
35
|
+
if (status === 'pause') pause()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
defineExpose({ play, pause })
|
|
39
|
+
|
|
17
40
|
const embedType = $computed<'YouTube' | 'Vimeo' | undefined>(() => {
|
|
18
41
|
const youtubeRegex = /youtube\.com|youtu\.be/
|
|
19
42
|
if (youtubeRegex.test(props.src || '')) return 'YouTube'
|
|
@@ -58,14 +81,16 @@ const videoUrl = $computed(() => {
|
|
|
58
81
|
/>
|
|
59
82
|
<video
|
|
60
83
|
v-else-if="src"
|
|
61
|
-
|
|
84
|
+
ref="video"
|
|
62
85
|
:autoplay="autoplay"
|
|
63
86
|
:muted="mute"
|
|
64
87
|
:loop="loop"
|
|
65
88
|
:style="{ aspectRatio }"
|
|
66
89
|
:controls="controls"
|
|
67
|
-
playsinline
|
|
68
|
-
|
|
90
|
+
:playsinline="playsinline"
|
|
91
|
+
>
|
|
92
|
+
<source :src="src" :type="`video/${videoFormat}`">
|
|
93
|
+
</video>
|
|
69
94
|
</div>
|
|
70
95
|
</template>
|
|
71
96
|
|
package/src/components/Btn.vue
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { MaterialIcons, ThemeType } from '@bagelink/vue'
|
|
3
3
|
import { MaterialIcon, Loading } from '@bagelink/vue'
|
|
4
|
-
import { useSlots } from 'vue'
|
|
4
|
+
import { useSlots, type SetupContext } from 'vue'
|
|
5
5
|
import { RouterLink } from 'vue-router'
|
|
6
6
|
|
|
7
7
|
const props = withDefaults(
|
|
@@ -54,7 +54,7 @@ const bind = $computed(() => {
|
|
|
54
54
|
}
|
|
55
55
|
return obj
|
|
56
56
|
})
|
|
57
|
-
const slots = useSlots()
|
|
57
|
+
const slots: SetupContext["slots"] = useSlots()
|
|
58
58
|
</script>
|
|
59
59
|
|
|
60
60
|
<template>
|
package/src/components/Image.vue
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { Skeleton, normalizeURL, normalizeDimension } from '@bagelink/vue'
|
|
2
|
+
import { Skeleton, normalizeURL, normalizeDimension, appendScript } from '@bagelink/vue'
|
|
3
3
|
import { watch } from 'vue'
|
|
4
4
|
|
|
5
|
+
declare global {
|
|
6
|
+
interface Window {
|
|
7
|
+
heic2any: any
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
const { height, width, alt = '', src } = defineProps<{
|
|
6
12
|
src: string
|
|
7
13
|
alt?: string
|
|
@@ -35,10 +41,10 @@ async function loadImage(src: string) {
|
|
|
35
41
|
}
|
|
36
42
|
}
|
|
37
43
|
try {
|
|
44
|
+
await appendScript('https://cdnjs.cloudflare.com/ajax/libs/heic2any/0.0.1/index.min.js')
|
|
38
45
|
const response = await fetch(src)
|
|
39
46
|
const blob = await response.blob()
|
|
40
|
-
const
|
|
41
|
-
const convertedBlob = await heic2any({ blob }) as Blob
|
|
47
|
+
const convertedBlob = await window.heic2any({ blob }) as Blob
|
|
42
48
|
imageSrc = URL.createObjectURL(convertedBlob)
|
|
43
49
|
// Only attempt to cache if the cache API is available
|
|
44
50
|
if ('caches' in window) {
|
|
@@ -72,9 +78,8 @@ watch(() => src, loadImage, { immediate: true })
|
|
|
72
78
|
<style scoped>
|
|
73
79
|
.img-web-kit {
|
|
74
80
|
max-width: 100%;
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
vertical-align: middle;
|
|
82
|
+
border: 0;
|
|
77
83
|
width: 100%;
|
|
78
|
-
|
|
79
84
|
}
|
|
80
85
|
</style>
|
package/src/components/Modal.vue
CHANGED
|
@@ -80,8 +80,8 @@ onUnmounted(() => {
|
|
|
80
80
|
:style="{ float: side ? 'left' : 'right' }"
|
|
81
81
|
flat
|
|
82
82
|
icon="close"
|
|
83
|
-
@click="closeModal"
|
|
84
83
|
thin
|
|
84
|
+
@click="closeModal"
|
|
85
85
|
/>
|
|
86
86
|
<Title v-if="title" class="modal-title" tag="h3" :label="title" />
|
|
87
87
|
</header>
|
|
@@ -113,6 +113,8 @@ onUnmounted(() => {
|
|
|
113
113
|
<style>
|
|
114
114
|
.modal{
|
|
115
115
|
color: var(--bgl-popup-text);
|
|
116
|
+
/* display: flex;
|
|
117
|
+
flex-direction: column; */
|
|
116
118
|
}
|
|
117
119
|
.modal-title {
|
|
118
120
|
text-align: center;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import {
|
|
3
3
|
BagelForm,
|
|
4
|
-
type
|
|
4
|
+
type BglFormSchemaFnT,
|
|
5
5
|
Btn,
|
|
6
6
|
type BtnOptions,
|
|
7
7
|
Modal,
|
|
@@ -14,7 +14,7 @@ const props = defineProps<{
|
|
|
14
14
|
width?: string
|
|
15
15
|
dismissable?: boolean
|
|
16
16
|
actions?: BtnOptions[]
|
|
17
|
-
schema:
|
|
17
|
+
schema: BglFormSchemaFnT<any>
|
|
18
18
|
|
|
19
19
|
onSubmit?: (formData: any) => Promise<void>
|
|
20
20
|
|
package/src/components/Pill.vue
CHANGED
|
@@ -86,7 +86,7 @@ const computedBackgroundColor = $computed(
|
|
|
86
86
|
<div v-if="loading" class="loading" />
|
|
87
87
|
<div v-else>
|
|
88
88
|
<div v-if="btn" class="flex h-100">
|
|
89
|
-
<Btn class="bgl_pill-btn" thin v-bind="btn" />
|
|
89
|
+
<Btn class="bgl_pill-btn" round thin v-bind="btn" />
|
|
90
90
|
</div>
|
|
91
91
|
</div>
|
|
92
92
|
<MaterialIcon v-if="icon" :icon="icon" />
|
|
@@ -98,7 +98,7 @@ const computedBackgroundColor = $computed(
|
|
|
98
98
|
<div v-if="loading" class="loading" />
|
|
99
99
|
<div v-else>
|
|
100
100
|
<div v-if="btnEnd" class="flex h-100">
|
|
101
|
-
<Btn class="bgl_pill-btn" thin v-bind="btnEnd" />
|
|
101
|
+
<Btn class="bgl_pill-btn" round thin v-bind="btnEnd" />
|
|
102
102
|
</div>
|
|
103
103
|
</div>
|
|
104
104
|
</div>
|
|
@@ -5,9 +5,13 @@ import {
|
|
|
5
5
|
MaterialIcon,
|
|
6
6
|
isDate,
|
|
7
7
|
keyToLabel,
|
|
8
|
-
useBglSchema
|
|
8
|
+
useBglSchema
|
|
9
9
|
} from '@bagelink/vue'
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
useVirtualList,
|
|
12
|
+
useIntersectionObserver,
|
|
13
|
+
until
|
|
14
|
+
} from '@vueuse/core'
|
|
11
15
|
import { computed, useSlots, watch } from 'vue'
|
|
12
16
|
|
|
13
17
|
export type SortDirectionsT = 'ASC' | 'DESC'
|
|
@@ -19,7 +23,7 @@ const {
|
|
|
19
23
|
showFields,
|
|
20
24
|
onLastItemVisible,
|
|
21
25
|
useServerSort = false,
|
|
22
|
-
selectable = false
|
|
26
|
+
selectable = false,
|
|
23
27
|
} = defineProps<{
|
|
24
28
|
data: T[]
|
|
25
29
|
schema?: BglFormSchemaT<T> | (() => BglFormSchemaT<T>)
|
|
@@ -36,55 +40,36 @@ const emit = defineEmits<{
|
|
|
36
40
|
'lastItemVisible': []
|
|
37
41
|
}>()
|
|
38
42
|
|
|
39
|
-
const
|
|
40
|
-
const loading = defineModel('loading', { default: false })
|
|
43
|
+
const NON_DIGIT_REGEX = /[^\d.-]/g
|
|
41
44
|
|
|
42
|
-
const
|
|
43
|
-
const computedItemHeight = $computed(() => `${itemHeight.value}px`)
|
|
44
|
-
|
|
45
|
-
let sortField = $ref('')
|
|
46
|
-
let sortDirection = $ref<SortDirectionsT>('ASC')
|
|
45
|
+
const slots: any = useSlots()
|
|
47
46
|
|
|
47
|
+
const loading = defineModel('loading', { default: false })
|
|
48
|
+
const itemHeight = defineModel('itemHeight', { default: 50 })
|
|
48
49
|
const selectedItems = defineModel<string[]>(
|
|
49
50
|
'selectedItems',
|
|
50
51
|
{
|
|
51
52
|
default: [] as string[],
|
|
52
|
-
set: (value: string[]) =>
|
|
53
|
-
setTimeout(updateAllSelectorState, 0)
|
|
54
|
-
return value
|
|
55
|
-
},
|
|
53
|
+
set: (value: string[]) => value,
|
|
56
54
|
}
|
|
57
55
|
)
|
|
58
56
|
|
|
59
|
-
|
|
57
|
+
let sortField = $ref('')
|
|
58
|
+
let sortDirection = $ref<SortDirectionsT>('ASC')
|
|
60
59
|
|
|
61
|
-
const
|
|
60
|
+
const allSelectorEl = $ref<HTMLInputElement | undefined>()
|
|
61
|
+
const lastItemEl = $ref<HTMLTableRowElement | null>()
|
|
62
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}`)
|
|
63
67
|
const computedSchema = $computed(() => useBglSchema<T>({
|
|
64
68
|
schema,
|
|
65
69
|
showFields,
|
|
66
70
|
data,
|
|
67
71
|
}))
|
|
68
72
|
|
|
69
|
-
function transform(rowData: any) {
|
|
70
|
-
const obj: { [key: string]: any } = { ...rowData }
|
|
71
|
-
const schemaFields = computedSchema.filter(f => f.id)
|
|
72
|
-
|
|
73
|
-
for (const field of schemaFields) {
|
|
74
|
-
const fieldData = rowData[`${field.id}`]
|
|
75
|
-
|
|
76
|
-
const newFieldVal = field.transform?.(fieldData, rowData)
|
|
77
|
-
|
|
78
|
-
Object.assign(obj, {
|
|
79
|
-
[`${field.id}`]: fieldData,
|
|
80
|
-
[`_transformed_${field.id}`]: newFieldVal,
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
return obj
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const NON_DIGIT_REGEX = /[^\d.-]/g
|
|
87
|
-
|
|
88
73
|
const computedData = computed(() => {
|
|
89
74
|
if (!sortField || useServerSort === true) return data.map(transform)
|
|
90
75
|
|
|
@@ -122,6 +107,23 @@ const computedData = computed(() => {
|
|
|
122
107
|
)
|
|
123
108
|
})
|
|
124
109
|
|
|
110
|
+
function transform(rowData: any) {
|
|
111
|
+
const obj: { [key: string]: any } = { ...rowData }
|
|
112
|
+
const schemaFields = computedSchema.filter(f => f.id)
|
|
113
|
+
|
|
114
|
+
for (const field of schemaFields) {
|
|
115
|
+
const fieldData = rowData[`${field.id}`]
|
|
116
|
+
|
|
117
|
+
const newFieldVal = field.transform?.(fieldData, rowData)
|
|
118
|
+
|
|
119
|
+
Object.assign(obj, {
|
|
120
|
+
[`${field.id}`]: fieldData,
|
|
121
|
+
[`_transformed_${field.id}`]: newFieldVal,
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
return obj
|
|
125
|
+
}
|
|
126
|
+
|
|
125
127
|
function sort(fieldname: string) {
|
|
126
128
|
if (sortField === fieldname) {
|
|
127
129
|
if (sortDirection === 'ASC') sortDirection = 'DESC'
|
|
@@ -130,11 +132,9 @@ function sort(fieldname: string) {
|
|
|
130
132
|
sortField = fieldname
|
|
131
133
|
sortDirection = 'ASC'
|
|
132
134
|
}
|
|
133
|
-
|
|
134
135
|
emit('orderBy', `${fieldname} ${sortDirection}`.trim() as EmitOrderT)
|
|
135
136
|
}
|
|
136
137
|
|
|
137
|
-
// #region ? VIRTUAL LIST
|
|
138
138
|
const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
|
|
139
139
|
computedData,
|
|
140
140
|
{
|
|
@@ -143,64 +143,53 @@ const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
|
|
|
143
143
|
}
|
|
144
144
|
)
|
|
145
145
|
|
|
146
|
-
watch(
|
|
147
|
-
() => computedData.value.length,
|
|
148
|
-
(newLength, oldLength) => {
|
|
149
|
-
if (newLength === oldLength || onLastItemVisible !== undefined) return
|
|
150
|
-
scrollTo(0)
|
|
151
|
-
}
|
|
152
|
-
)
|
|
153
|
-
// #endregion ? VIRTUAL LIST
|
|
154
|
-
|
|
155
|
-
// #region ? SELECT COLUMN
|
|
156
|
-
const allSelector = $ref<HTMLInputElement | undefined>()
|
|
157
|
-
const computedSelectedItems = $computed(() => selectedItems.value)
|
|
158
|
-
|
|
159
146
|
function updateAllSelectorState() {
|
|
160
|
-
if (!
|
|
161
|
-
const allSelected
|
|
162
|
-
= computedData.value.length === computedSelectedItems.length
|
|
147
|
+
if (!allSelectorEl) return
|
|
148
|
+
const allSelected = computedData.value.length === computedSelectedItems.length
|
|
163
149
|
&& computedData.value.every(s => s.id && computedSelectedItems.includes(s.id))
|
|
164
|
-
|
|
165
|
-
|
|
150
|
+
allSelectorEl.checked = allSelected
|
|
151
|
+
allSelectorEl.indeterminate = !allSelected && computedSelectedItems.length > 0
|
|
166
152
|
}
|
|
167
153
|
|
|
168
154
|
function toggleSelectItem(item: { [key: string]: any }) {
|
|
169
155
|
if (computedSelectedItems.length === 0) {
|
|
170
156
|
const obj = { ...item }
|
|
171
|
-
Object.keys(obj).forEach(
|
|
172
|
-
if (key.startsWith('_transformed_')) delete obj[key]
|
|
173
|
-
})
|
|
174
|
-
|
|
157
|
+
Object.keys(obj).forEach(key => key.startsWith('_transformed_') && delete obj[key])
|
|
175
158
|
emit('select', obj as T)
|
|
176
159
|
return
|
|
177
160
|
}
|
|
178
161
|
const index = computedSelectedItems.indexOf(item.id)
|
|
179
|
-
|
|
180
|
-
computedSelectedItems.splice(index, 1)
|
|
181
|
-
} else {
|
|
182
|
-
computedSelectedItems.push(item.id)
|
|
183
|
-
}
|
|
162
|
+
index > -1 ? computedSelectedItems.splice(index, 1) : computedSelectedItems.push(item.id)
|
|
184
163
|
}
|
|
185
164
|
|
|
186
165
|
function toggleSelectAll(event: Event) {
|
|
187
166
|
const value = (event.target as HTMLInputElement).checked
|
|
188
167
|
selectedItems.value = value ? computedData.value.map((d: any) => d.id) : []
|
|
189
168
|
}
|
|
190
|
-
// #endregion ? SELECT COLUMN
|
|
191
|
-
|
|
192
|
-
const lastItem = $ref<HTMLTableRowElement | null>()
|
|
193
169
|
|
|
194
170
|
async function registerLastItemObserver() {
|
|
195
|
-
await until(() =>
|
|
171
|
+
await until(() => lastItemEl).toBeTruthy()
|
|
196
172
|
|
|
197
|
-
useIntersectionObserver(
|
|
173
|
+
useIntersectionObserver(lastItemEl, ([entry]) => {
|
|
198
174
|
if (entry.isIntersecting && computedData.value.length) {
|
|
199
175
|
emit('lastItemVisible')
|
|
200
176
|
}
|
|
201
177
|
})
|
|
202
178
|
}
|
|
203
179
|
|
|
180
|
+
watch(
|
|
181
|
+
() => computedData.value.length,
|
|
182
|
+
(newLength, oldLength) => {
|
|
183
|
+
if (newLength === oldLength || onLastItemVisible !== undefined) return
|
|
184
|
+
scrollTo(0)
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
watch(
|
|
189
|
+
() => computedSelectedItems.length,
|
|
190
|
+
() => { updateAllSelectorState() }
|
|
191
|
+
)
|
|
192
|
+
|
|
204
193
|
watch(
|
|
205
194
|
() => loading.value,
|
|
206
195
|
(newLoadingVal, oldLoadingVal) => {
|
|
@@ -228,7 +217,7 @@ watch(
|
|
|
228
217
|
<thead class="row first-row">
|
|
229
218
|
<th v-if="isSelectable">
|
|
230
219
|
<input
|
|
231
|
-
ref="
|
|
220
|
+
ref="allSelectorEl"
|
|
232
221
|
type="checkbox"
|
|
233
222
|
@click.stop
|
|
234
223
|
@change="toggleSelectAll"
|
|
@@ -292,7 +281,7 @@ watch(
|
|
|
292
281
|
</div>
|
|
293
282
|
</td>
|
|
294
283
|
</tr>
|
|
295
|
-
<tr v-if="onLastItemVisible !== undefined" ref="
|
|
284
|
+
<tr v-if="onLastItemVisible !== undefined" ref="lastItemEl" style="height: 1px;" />
|
|
296
285
|
</tbody>
|
|
297
286
|
</table>
|
|
298
287
|
</div>
|
|
@@ -66,7 +66,8 @@ function setFieldData(key: string, val: any) {
|
|
|
66
66
|
return data
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
function getFieldData(obj:
|
|
69
|
+
function getFieldData(obj: any, key: string) {
|
|
70
|
+
if (typeof obj !== 'object' || obj === null) return obj
|
|
70
71
|
const keys = key.split(objPathRegex)
|
|
71
72
|
let result = obj
|
|
72
73
|
for (const k of keys) {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { EditorState, Modal } from '../richTextTypes'
|
|
2
|
+
import { bagelFormUtils } from '@bagelink/vue'
|
|
3
|
+
|
|
4
|
+
const { frmRow, numField } = bagelFormUtils
|
|
2
5
|
|
|
3
6
|
export function insertImage(modal: Modal, state: EditorState) {
|
|
4
7
|
const { range, doc } = state
|
|
@@ -9,15 +12,34 @@ export function insertImage(modal: Modal, state: EditorState) {
|
|
|
9
12
|
schema: [
|
|
10
13
|
{ id: 'src', $el: 'file', attrs: { bindkey: 'url' } },
|
|
11
14
|
{ id: 'alt', $el: 'text', label: 'Alt Text' },
|
|
15
|
+
frmRow(
|
|
16
|
+
numField('width', 'Width', { min: 1 }),
|
|
17
|
+
numField('height', 'Height', { min: 1 }),
|
|
18
|
+
),
|
|
19
|
+
{ id: 'figcaption', $el: 'check', label: 'Show Caption' },
|
|
12
20
|
],
|
|
13
|
-
onSubmit: (data:
|
|
21
|
+
onSubmit: (data: Record<string, any>) => {
|
|
14
22
|
if (data.src) {
|
|
15
23
|
const img = doc.createElement('img')
|
|
16
|
-
img
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
Object.assign(img, {
|
|
25
|
+
src: data.src,
|
|
26
|
+
alt: data.alt || '',
|
|
27
|
+
width: data.width || undefined,
|
|
28
|
+
height: data.height || undefined
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const node = data.figcaption
|
|
32
|
+
? (() => {
|
|
33
|
+
const figcaption = doc.createElement('figcaption')
|
|
34
|
+
figcaption.textContent = data.alt
|
|
35
|
+
const figure = doc.createElement('figure')
|
|
36
|
+
figure.append(img, figcaption)
|
|
37
|
+
return figure
|
|
38
|
+
})()
|
|
39
|
+
: img
|
|
40
|
+
|
|
19
41
|
range.collapse(false)
|
|
20
|
-
range.insertNode(
|
|
42
|
+
range.insertNode(node)
|
|
21
43
|
}
|
|
22
44
|
}
|
|
23
45
|
})
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import { onMounted, watch } from 'vue'
|
|
13
13
|
import 'floating-vue/style.css'
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
interface PropTypes {
|
|
16
16
|
options: Option[]
|
|
17
17
|
placeholder?: string
|
|
18
18
|
disabled?: boolean
|
|
@@ -28,11 +28,16 @@ const props = defineProps<{
|
|
|
28
28
|
clearable?: boolean
|
|
29
29
|
searchPlaceholder?: string
|
|
30
30
|
onSearch?: (search: string) => Promise<Option[]>
|
|
31
|
-
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const props = withDefaults(defineProps<PropTypes>(), {
|
|
34
|
+
searchPlaceholder: 'Search',
|
|
35
|
+
placeholder: 'Select',
|
|
36
|
+
})
|
|
32
37
|
|
|
33
38
|
const emit = defineEmits(['update:modelValue']) // Add 'search' event
|
|
34
39
|
|
|
35
|
-
const searchPlaceholder = $computed(() => props.searchPlaceholder
|
|
40
|
+
const searchPlaceholder = $computed(() => props.searchPlaceholder)
|
|
36
41
|
|
|
37
42
|
const searchInput = $ref<HTMLElement | undefined>()
|
|
38
43
|
|
|
@@ -46,7 +51,7 @@ let selected = $ref(false)
|
|
|
46
51
|
let open = $ref(false)
|
|
47
52
|
|
|
48
53
|
const selectedLabel = $computed((): string => {
|
|
49
|
-
if (selectedItemCount === 0) return props.placeholder
|
|
54
|
+
if (selectedItemCount === 0) return props.placeholder
|
|
50
55
|
if (selectedItemCount > 4) {
|
|
51
56
|
const str = selectedItems
|
|
52
57
|
.slice(0, 4)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import type { NavLink } from '@bagelink/vue'
|
|
3
|
-
import { Btn, Card, Icon } from '@bagelink/vue'
|
|
3
|
+
import { Btn, Card, Icon, useDebounceFn } from '@bagelink/vue'
|
|
4
4
|
import { useSlots } from 'vue'
|
|
5
5
|
|
|
6
6
|
const props = defineProps<{
|
|
@@ -14,11 +14,10 @@ const emit = defineEmits(['update:open'])
|
|
|
14
14
|
const slots = useSlots()
|
|
15
15
|
|
|
16
16
|
let isOpen = $ref(props.open)
|
|
17
|
-
|
|
18
|
-
function toggleMenu() {
|
|
17
|
+
const toggleMenu = useDebounceFn(() => {
|
|
19
18
|
isOpen = !isOpen
|
|
20
19
|
emit('update:open', isOpen)
|
|
21
|
-
}
|
|
20
|
+
})
|
|
22
21
|
</script>
|
|
23
22
|
|
|
24
23
|
<template>
|
|
@@ -43,7 +42,6 @@ function toggleMenu() {
|
|
|
43
42
|
v-tooltip.right="{
|
|
44
43
|
content: nav.label,
|
|
45
44
|
disabled: open,
|
|
46
|
-
class: ['nav-tooltip'],
|
|
47
45
|
}"
|
|
48
46
|
:to="nav.to"
|
|
49
47
|
class="nav-button px-075 me-auto w-100"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type { VNode } from 'vue'
|
|
2
|
+
import type { SetupContext, VNode } from 'vue'
|
|
3
3
|
import { type Tab, TabsNav } from '@bagelink/vue'
|
|
4
4
|
import { defineComponent, h, useSlots } from 'vue'
|
|
5
5
|
|
|
@@ -12,7 +12,7 @@ const props = defineProps<{
|
|
|
12
12
|
}>()
|
|
13
13
|
|
|
14
14
|
const emit = defineEmits(['update:modelValue'])
|
|
15
|
-
const slots = useSlots()
|
|
15
|
+
const slots: SetupContext["slots"] = useSlots()
|
|
16
16
|
const group = Math.random().toString(36).slice(7)
|
|
17
17
|
const { currentTab } = useTabs(group)
|
|
18
18
|
|
|
@@ -34,7 +34,9 @@ function updateIndicator() {
|
|
|
34
34
|
if (activeTab && tabsWrap.value) {
|
|
35
35
|
const { offsetLeft, offsetWidth } = activeTab
|
|
36
36
|
tabsWrap.value.style.setProperty('--indicator-left', `${offsetLeft}px`)
|
|
37
|
-
tabsWrap.value
|
|
37
|
+
if (tabsWrap.value) {
|
|
38
|
+
tabsWrap.value.style.setProperty('--indicator-width', `${offsetWidth}px`)
|
|
39
|
+
}
|
|
38
40
|
}
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -75,7 +77,7 @@ onBeforeUnmount(() => {
|
|
|
75
77
|
|
|
76
78
|
<template>
|
|
77
79
|
<div
|
|
78
|
-
ref="tabsWrap" class="grid auto-flow-columns relative fit-content bgl_tabs_wrap"
|
|
80
|
+
ref="tabsWrap" class="grid auto-flow-columns relative fit-content bgl_tabs_wrap overflow-hidden"
|
|
79
81
|
:class="{ 'bgl_flat-tabs': flat, 'bgl_vertical-tabs': vertical }"
|
|
80
82
|
>
|
|
81
83
|
<button
|