@bagelink/vue 0.0.819 → 0.0.823

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/vue",
3
3
  "type": "module",
4
- "version": "0.0.819",
4
+ "version": "0.0.823",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Neveh Allon",
@@ -25,6 +25,7 @@ const props = defineProps({
25
25
  })
26
26
  const emit = defineEmits(['update:index'])
27
27
  const bglSlider = $ref<HTMLElement>()
28
+ let itemCount = $ref(props.items)
28
29
  let activeSlideIndex = $ref(0)
29
30
  let isDragging = $ref(false)
30
31
  let startX = $ref(0)
@@ -55,6 +56,7 @@ function evalHeight() {
55
56
 
56
57
  function scrollEase(target: number, duration: number = 500) {
57
58
  if (!bglSlider) return
59
+ if (isPressed) return
58
60
  const start = bglSlider.scrollLeft
59
61
  const change = target - start
60
62
  const startTime = performance.now()
@@ -103,23 +105,29 @@ watch(
103
105
  emit('update:index', activeSlideIndex) }
104
106
  )
105
107
 
106
- function scrollEnd() {
107
- const slider = bglSlider
108
- if (!slider || props.items !== 1) return
109
- const nextSlide = Math.round(slider.scrollLeft / slider.offsetWidth)
108
+ function goToSlideScrollEnd() {
109
+ if (!bglSlider) return
110
+ const isDragForward = startX > bglSlider.offsetLeft
111
+ if (itemCount === 1) {
112
+ if (isDragForward) activeSlideIndex = Math.ceil(bglSlider.scrollLeft / bglSlider.offsetWidth)
113
+ else activeSlideIndex = Math.floor(bglSlider.scrollLeft / bglSlider.offsetWidth)
114
+ } else {
115
+ activeSlideIndex = Math.round(bglSlider.scrollLeft / bglSlider.offsetWidth)
116
+ }
117
+ startX = 0
118
+ const nextSlide = Math.round(bglSlider.scrollLeft / bglSlider.offsetWidth)
110
119
  goToSlide(nextSlide)
111
120
  }
112
121
 
122
+ let timeout: any = null
123
+
124
+ function scrollEnd() {
125
+ if (timeout) clearTimeout(timeout)
126
+ timeout = setTimeout(goToSlideScrollEnd, 400)
127
+ }
128
+
113
129
  function stopDragging(e: MouseEvent) {
114
130
  isPressed = false
115
- const slider = bglSlider
116
- if (!slider) return
117
- const isDragForward = startX > e.pageX + slider.offsetLeft
118
- if (isDragForward)
119
- activeSlideIndex = Math.ceil(slider.scrollLeft / slider.offsetWidth)
120
- else activeSlideIndex = Math.floor(slider.scrollLeft / slider.offsetWidth)
121
- if (props.items === 1) goToSlide(activeSlideIndex)
122
- startX = 0
123
131
  document.removeEventListener('mousemove', move)
124
132
  document.removeEventListener('mouseup', stopDragging)
125
133
  document.removeEventListener('dragend', stopDragging)
@@ -143,7 +151,7 @@ function startDragging(e: MouseEvent) {
143
151
  isPressed = true
144
152
  document.addEventListener('mousemove', move)
145
153
  document.addEventListener('mouseup', stopDragging)
146
- document.addEventListener('dragend', stopDragging)
154
+ // document.addEventListener('dragend', stopDragging)
147
155
  }
148
156
 
149
157
  function next() {
@@ -163,12 +171,18 @@ function prev() {
163
171
 
164
172
  function evalWidth() {
165
173
  if (!bglSlider) return
174
+ if (window.innerWidth < 600) itemCount = 1
175
+ else if (window.innerWidth < 991) itemCount = Math.min(props.items, 2)
176
+ else if (window.innerWidth < 1280) itemCount = Math.min(props.items, 3)
177
+ else itemCount = props.items
178
+ console.log(itemCount, window.innerWidth)
166
179
  goToSlide(activeSlideIndex)
167
180
  }
168
181
 
169
182
  onMounted(() => {
170
183
  window.addEventListener('resize', evalWidth)
171
184
  evalHeight()
185
+ evalWidth()
172
186
  disableDrag()
173
187
  })
174
188
 
@@ -182,10 +196,15 @@ onUnmounted(() => { window.removeEventListener('resize', evalWidth) })
182
196
  :class="{
183
197
  dragging: isDragging,
184
198
  clicking: isPressed,
185
- [`slides-${items}`]: true,
186
199
  allowScroll,
200
+ odd: itemCount % 2 !== 0,
201
+ [`slides-${itemCount}`]: itemCount === 1,
187
202
  }"
188
203
  class="bgl-slider"
204
+ :style="{
205
+ '--item-count': itemCount,
206
+ }"
207
+ @scroll.active="scrollEnd"
189
208
  @mousedown="startDragging"
190
209
  >
191
210
  <div v-if="isDragging" class="blocker" />
@@ -230,33 +249,18 @@ onUnmounted(() => { window.removeEventListener('resize', evalWidth) })
230
249
  overflow-x: scroll;
231
250
  }
232
251
 
233
- .bgl-slider.slides-6 {
234
- grid-auto-columns: 15.9%;
235
- gap: 1%;
236
- }
237
-
238
- .bgl-slider.slides-5 {
239
- grid-auto-columns: 19.3%;
240
- gap: 1%;
241
- }
242
-
243
- .bgl-slider.slides-4 {
244
- grid-auto-columns: 24.3%;
245
- gap: 1%;
246
- }
247
-
248
- .bgl-slider.slides-3 {
249
- grid-auto-columns: 33%;
252
+ .bgl-slider {
253
+ grid-auto-columns: calc(100% / var(--item-count) - calc(var(--item-count) - 1) * 1%);
250
254
  gap: 1%;
251
255
  }
252
256
 
253
- .bgl-slider.slides-2 {
254
- grid-auto-columns: 50%;
255
- gap: 1%;
257
+ .bgl-slider.odd {
258
+ grid-auto-columns: calc(100% / var(--item-count) - calc(var(--item-count) - 2) * 1%);
256
259
  }
257
260
 
258
261
  .bgl-slider.slides-1 {
259
262
  grid-auto-columns: 100%;
263
+ gap: 0;
260
264
  }
261
265
 
262
266
  .bgl-slider::-webkit-scrollbar {
@@ -282,21 +286,10 @@ onUnmounted(() => { window.removeEventListener('resize', evalWidth) })
282
286
  user-select: none;
283
287
  }
284
288
 
285
- @media screen and (max-width: 1280px) {
286
- .bgl-slider.slides-4 {
287
- grid-auto-columns: 33%;
288
- }
289
- }
290
-
291
- @media screen and (max-width: 991px) {
292
- .bgl-slider.slides-4 {
293
- grid-auto-columns: 50%;
294
- }
295
- }
296
-
297
289
  @media screen and (max-width: 600px) {
298
- .bgl-slider.slides-4 {
299
- grid-auto-columns: 100%;
290
+ .bgl-slider.slides-1 * {
291
+ margin-inline-start: 1%;
300
292
  }
293
+
301
294
  }
302
295
  </style>
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
- import type { MaterialIcons, ThemeType } from '@bagelink/vue'
3
2
  import { Btn, MaterialIcon } from '@bagelink/vue'
4
3
  import { useSlots } from 'vue'
4
+ import type { MaterialIcons, ThemeType } from '@bagelink/vue'
5
5
 
6
6
  interface BtnProp {
7
7
  icon: MaterialIcons
@@ -8,7 +8,7 @@ import {
8
8
  useBglSchema,
9
9
  } from '@bagelink/vue'
10
10
  import { useVirtualList } from '@vueuse/core'
11
- import { computed, useSlots, watch } from 'vue'
11
+ import { computed, useSlots, watch, onMounted, onUnmounted } from 'vue'
12
12
 
13
13
  export type SortDirectionsT = 'ASC' | 'DESC'
14
14
  export type EmitOrderT = `${string} ${SortDirectionsT}`
@@ -17,6 +17,7 @@ const {
17
17
  data,
18
18
  schema,
19
19
  showFields,
20
+ onLastItemVisible,
20
21
  useServerSort = false,
21
22
  selectable = false
22
23
  } = defineProps<{
@@ -25,6 +26,7 @@ const {
25
26
  showFields?: string[]
26
27
  useServerSort?: boolean
27
28
  selectable?: boolean
29
+ onLastItemVisible?: () => void
28
30
  }>()
29
31
 
30
32
  const emit = defineEmits<{
@@ -141,7 +143,7 @@ const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
141
143
  watch(
142
144
  () => computedData.value.length,
143
145
  (newLength, oldLength) => {
144
- if (newLength === oldLength) return
146
+ if (newLength === oldLength || onLastItemVisible !== undefined) return
145
147
  scrollTo(0)
146
148
  }
147
149
  )
@@ -183,6 +185,18 @@ function toggleSelectAll(event: Event) {
183
185
  selectedItems.value = value ? computedData.value.map((d: any) => d.id) : []
184
186
  }
185
187
  // #endregion ? SELECT COLUMN
188
+ const lastItem = $ref<HTMLTableRowElement | null>()
189
+ let observer: IntersectionObserver | undefined
190
+
191
+ onMounted(() => {
192
+ if (onLastItemVisible === undefined) return
193
+ observer = new IntersectionObserver(([entry]) => {
194
+ if (entry.isIntersecting && computedData.value.length) onLastItemVisible()
195
+ })
196
+ if (lastItem) observer.observe(lastItem)
197
+ })
198
+
199
+ onUnmounted(() => { observer?.disconnect() })
186
200
  </script>
187
201
 
188
202
  <template>
@@ -266,6 +280,7 @@ function toggleSelectAll(event: Event) {
266
280
  </div>
267
281
  </td>
268
282
  </tr>
283
+ <tr v-if="onLastItemVisible !== undefined" ref="lastItem" />
269
284
  </tbody>
270
285
  </table>
271
286
  </div>
@@ -270,7 +270,16 @@ function drop(e: DragEvent) {
270
270
  class="single-image-item-preview"
271
271
  :class="{ 'bgl_fill-image': fill }"
272
272
  >
273
- <img v-if="isImage(file.extension || file.url)" v-lightbox class="single-preview" :src="file.url" alt="">
273
+ <div v-if="isImage(file.extension || file.url)" class="h-100">
274
+ <img v-lightbox class="single-preview" :src="file.url" alt="">
275
+ <div class="position-start m-05 gap-025 flex">
276
+ <Btn v-tooltip="'Delete'" color="gray" thin icon="delete" @click.stop="removeFile(file)" />
277
+ <Btn
278
+ v-tooltip="'Replace'" color="gray" thin icon="autorenew"
279
+ @click="browse"
280
+ />
281
+ </div>
282
+ </div>
274
283
  <Icon v-else :size="4" weight="2" icon="draft" class="color-primary w-100" />
275
284
  </div>
276
285
 
@@ -407,8 +416,21 @@ function drop(e: DragEvent) {
407
416
  height: calc(100% - 2rem);
408
417
  object-fit: cover;
409
418
  background: var(--bgl-gray-light);
410
- box-shadow: 0 0 10px #00000012;
411
419
  }
420
+ .single-image-item-preview:hover::after{
421
+ content: "zoom_in";
422
+ font-size: 32px;
423
+ font-family: "Material Symbols Outlined", serif;
424
+ position: absolute;
425
+ border-radius: 100%;
426
+ color: var(--bgl-white);
427
+ z-index: 9;
428
+ pointer-events: none;
429
+ }
430
+ .single-image-item-preview:hover img{
431
+ filter: brightness(70%);
432
+ }
433
+
412
434
  .bgl_fill-image.single-image-item-preview {
413
435
  height: 100%;
414
436
  }
@@ -0,0 +1,52 @@
1
+ <script lang="ts" setup>
2
+ const {
3
+ count = 1,
4
+ height = '50px',
5
+ width = 'auto',
6
+ borderRadius = 'var(--skeleton-radius)',
7
+ } = defineProps<{
8
+ count?: number
9
+ height?: string
10
+ width?: string
11
+ borderRadius?: string
12
+ }>()
13
+ import { useSlots } from 'vue'
14
+
15
+ const slots = useSlots()
16
+ </script>
17
+
18
+ <template>
19
+ <template v-for="i in count" :key="i">
20
+ <div v-if="slots.default" class="skeleton-wrap">
21
+ <slot />
22
+ </div>
23
+ <div v-else class="skeleton" :style="{ height, width, borderRadius }" />
24
+ </template>
25
+ </template>
26
+
27
+ <style scoped>
28
+ .skeleton-wrap {
29
+ --skeleton-radius: 0.25rem;
30
+ --skeleton-bg: #f0f0f0;
31
+ --skeleton-margin: 1rem;
32
+ margin-bottom: var(--skeleton-margin);
33
+ }
34
+ .skeleton {
35
+ animation: skeleton 2s infinite;
36
+ background-color: var(--skeleton-bg);
37
+ border-radius: var(--bg-card-radius);
38
+ margin-bottom: 0.25em;
39
+ }
40
+
41
+ @keyframes skeleton {
42
+ 0% {
43
+ opacity: 0.5;
44
+ }
45
+ 50% {
46
+ opacity: 1;
47
+ }
48
+ 100% {
49
+ opacity: 0.5;
50
+ }
51
+ }
52
+ </style>
@@ -1,6 +1,7 @@
1
1
  export { default as BottomMenu } from './BottomMenu.vue'
2
2
  export { default as Layout } from './Layout.vue'
3
3
  export { default as SidebarMenu } from './SidebarMenu.vue'
4
+ export { default as Skeleton } from './Skeleton.vue'
4
5
  export { default as TabbedLayout } from './TabbedLayout.vue'
5
6
  export { default as Tabs } from './Tabs.vue'
6
7
  export { default as TabsBody } from './TabsBody.vue'
@@ -1,24 +1,34 @@
1
1
  import type { Attributes, BglFormSchemaT } from '@bagelink/vue'
2
2
 
3
- export function debounce<T extends (...args: any[]) => void>(func: T, wait: number): (...args: Parameters<T>) => void {
4
- let timeoutId: ReturnType<typeof setTimeout> | undefined
5
-
6
- return function (...args: Parameters<T>) {
7
- if (timeoutId) {
8
- clearTimeout(timeoutId)
3
+ export function debounce<T extends (...args: Parameters<T>) => ReturnType<T>>(
4
+ callback: T,
5
+ wait: number,
6
+ immediate?: boolean
7
+ ): (...args: Parameters<T>) => void {
8
+ let timeout: NodeJS.Timeout | undefined
9
+
10
+ return function (this: ThisParameterType<T>, ...args: Parameters<T>): void {
11
+ const later = () => {
12
+ timeout = undefined
13
+ if (!immediate) callback.apply(this, args)
9
14
  }
10
15
 
11
- timeoutId = setTimeout(() => {
12
- func(...args)
13
- }, wait)
16
+ const callNow = immediate && timeout === undefined
17
+ clearTimeout(timeout)
18
+ timeout = setTimeout(later, wait)
19
+
20
+ if (callNow) callback.apply(this, args)
14
21
  }
15
22
  }
16
23
 
17
24
  export function keyToLabel(key?: string): string | undefined {
18
25
  if (key === undefined) return key
19
- return key.split('_')
20
- .map(k => k.charAt(0).toUpperCase() + k.slice(1))
21
- .join(' ') || key
26
+ return (
27
+ key
28
+ .split('_')
29
+ .map(k => k.charAt(0).toUpperCase() + k.slice(1))
30
+ .join(' ') || key
31
+ )
22
32
  }
23
33
 
24
34
  export async function copyText(text: string, cb?: (msg: string) => void) {
@@ -38,19 +48,27 @@ export function useEscape(event: KeyboardEvent, closeModel: () => void) {
38
48
  }
39
49
 
40
50
  export function classify(fieldVal?: any, row?: any, ...classes: any[]) {
41
- return classes.map((cls) => {
42
- if (typeof cls === 'function') return cls(fieldVal, row)
43
- return cls
44
- }).join(' ')
51
+ return classes
52
+ .map((cls) => {
53
+ if (typeof cls === 'function') return cls(fieldVal, row)
54
+ return cls
55
+ })
56
+ .join(' ')
45
57
  }
46
58
 
47
- export function bindAttrs<T = { [key: string]: any }>(attrs?: Attributes, fieldVal?: any, row?: T) {
59
+ export function bindAttrs<T = { [key: string]: any }>(
60
+ attrs?: Attributes,
61
+ fieldVal?: any,
62
+ row?: T
63
+ ) {
48
64
  if (!attrs) return {}
49
65
  const exclude = ['class']
50
- const arr = Object.entries(attrs).filter(([key]) => !exclude.includes(key)).map(([key, value]) => [
51
- key,
52
- typeof value === 'function' ? value(fieldVal, row) : value,
53
- ])
66
+ const arr = Object.entries(attrs)
67
+ .filter(([key]) => !exclude.includes(key))
68
+ .map(([key, value]) => [
69
+ key,
70
+ typeof value === 'function' ? value(fieldVal, row) : value,
71
+ ])
54
72
 
55
73
  const resolvedAttrs = Object.fromEntries(arr)
56
74
  return resolvedAttrs
@@ -60,7 +78,8 @@ export function iffer(field: any, itemData: any) {
60
78
  if (field['v-if'] === undefined) return true
61
79
  if (typeof field['v-if'] === 'boolean') return field['v-if']
62
80
  if (typeof field['v-if'] === 'string') return true
63
- if (typeof field['v-if'] === 'function') return field['v-if']?.(itemData?.[field.id], itemData)
81
+ if (typeof field['v-if'] === 'function')
82
+ return field['v-if']?.(itemData?.[field.id], itemData)
64
83
  return true
65
84
  }
66
85
 
@@ -77,22 +96,25 @@ export { useLang } from './lang'
77
96
 
78
97
  export { formatString } from './strings'
79
98
 
80
- export function getFallbackSchema<T>(data?: any[], showFields?: string[]): BglFormSchemaT<T> {
99
+ export function getFallbackSchema<T>(
100
+ data?: any[],
101
+ showFields?: string[]
102
+ ): BglFormSchemaT<T> {
81
103
  const keys = Array.from(new Set((data ?? []).flatMap(Object.keys)))
82
104
 
83
- const schema: BglFormSchemaT<T> = keys.map(
84
- id => ({
85
- id,
86
- label: keyToLabel(id),
87
- })
88
- )
105
+ const schema: BglFormSchemaT<T> = keys.map(id => ({
106
+ id,
107
+ label: keyToLabel(id),
108
+ }))
89
109
 
90
110
  return showFields
91
111
  ? schema.filter(f => showFields.includes(f.id as string) || !f.id)
92
112
  : schema
93
113
  }
94
114
 
95
- export const sleep = (ms: number = 100) => new Promise(resolve => setTimeout(resolve, ms))
115
+ export function sleep(ms: number = 100) {
116
+ return new Promise(resolve => setTimeout(resolve, ms))
117
+ }
96
118
 
97
119
  export function appendScript(src: string): Promise<void> {
98
120
  return new Promise((resolve, reject) => {
@@ -102,7 +124,9 @@ export function appendScript(src: string): Promise<void> {
102
124
  }
103
125
  const script = document.createElement('script')
104
126
  script.src = src
105
- script.onload = () => { resolve() }
127
+ script.onload = () => {
128
+ resolve()
129
+ }
106
130
  script.onerror = reject
107
131
  document.head.append(script)
108
132
  })