@bagelink/vue 0.0.1031 → 0.0.1035

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.
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { Skeleton, normalizeDimension, appendScript } from '@bagelink/vue'
2
+ import { Skeleton, normalizeDimension, appendScript, useBagel } from '@bagelink/vue'
3
3
  import { watch } from 'vue'
4
4
 
5
5
  declare global {
@@ -8,8 +8,9 @@ declare global {
8
8
  }
9
9
  }
10
10
 
11
- const { height, width, alt = '', src } = defineProps<{
12
- src: string
11
+ const { height, width, alt = '', src, pathKey } = defineProps<{
12
+ src?: string
13
+ pathKey?: string
13
14
  alt?: string
14
15
  width?: string | number
15
16
  height?: string | number
@@ -18,12 +19,18 @@ const { height, width, alt = '', src } = defineProps<{
18
19
 
19
20
  let imageSrc = $ref<string | null>(null)
20
21
 
21
- async function loadImage(src: string) {
22
- if (!src) {
22
+ const bagel = useBagel()
23
+ const fileBaseUrl = $computed(() => (bagel.fileBaseUrl || 'https://files.bagel.design').replace(/\/$/, ''))
24
+ const pathToUrl = () => `${fileBaseUrl}/${pathKey}`
25
+
26
+ async function loadImage() {
27
+ const url = src || pathToUrl()
28
+ console.log(url)
29
+ if (!url) {
23
30
  imageSrc = null
24
31
  return
25
32
  }
26
- const ext = src.split('.').pop()?.toLowerCase().split('?').shift()
33
+ const ext = url.split('.').pop()?.toLowerCase().split('?').shift()
27
34
 
28
35
  if (ext === 'heic') {
29
36
  if (!('caches' in window)) {
@@ -31,7 +38,7 @@ async function loadImage(src: string) {
31
38
  } else {
32
39
  try {
33
40
  const imgCache = await window.caches.open('img-cache')
34
- const cachedResponse = await imgCache.match(src)
41
+ const cachedResponse = await imgCache.match(url)
35
42
  if (cachedResponse) {
36
43
  imageSrc = URL.createObjectURL(await cachedResponse.blob())
37
44
  return
@@ -42,7 +49,7 @@ async function loadImage(src: string) {
42
49
  }
43
50
  try {
44
51
  await appendScript('https://cdnjs.cloudflare.com/ajax/libs/heic2any/0.0.1/index.min.js')
45
- const response = await fetch(src)
52
+ const response = await fetch(url)
46
53
  const blob = await response.blob()
47
54
  const convertedBlob = await window.heic2any({ blob }) as Blob
48
55
  imageSrc = URL.createObjectURL(convertedBlob)
@@ -50,7 +57,7 @@ async function loadImage(src: string) {
50
57
  if ('caches' in window) {
51
58
  try {
52
59
  const imgCache = await window.caches.open('img-cache')
53
- imgCache.put(src, new Response(convertedBlob))
60
+ imgCache.put(url, new Response(convertedBlob))
54
61
  } catch (cacheError) {
55
62
  console.warn('Failed to cache the image:', cacheError)
56
63
  }
@@ -59,10 +66,10 @@ async function loadImage(src: string) {
59
66
  console.error('Error converting HEIC file:', error)
60
67
  }
61
68
  } else {
62
- imageSrc = src
69
+ imageSrc = url
63
70
  }
64
71
  }
65
- watch(() => src, loadImage, { immediate: true })
72
+ watch(() => [src, pathKey], loadImage, { immediate: true })
66
73
  </script>
67
74
 
68
75
  <template>
@@ -11,7 +11,9 @@ import {
11
11
  ToggleInput,
12
12
  bindAttrs,
13
13
  classify,
14
- BagelForm
14
+ BagelForm,
15
+ NumberInput,
16
+ UploadInput,
15
17
  } from '@bagelink/vue'
16
18
  import TabsNav from '../layout/TabsNav.vue'
17
19
 
@@ -35,11 +37,13 @@ const is = $computed(() => {
35
37
  customAttrs.multiline = true
36
38
  return TextInput
37
39
  }
40
+ if (props.field.$el === 'number') return NumberInput
38
41
  if (props.field.$el === 'array') return FieldArray
39
42
  if (props.field.$el === 'select') return SelectInput
40
43
  if (props.field.$el === 'toggle') return ToggleInput
41
44
  if (props.field.$el === 'check') return CheckInput
42
45
  if (props.field.$el === 'richtext') return RichText
46
+ if (props.field.$el === 'upload') return UploadInput
43
47
  if (props.field.$el === 'file') return FileUpload
44
48
  if (props.field.$el === 'date') return DateInput
45
49
  if (props.field.$el === 'tabs') return TabsNav
@@ -5,7 +5,7 @@ import { watch } from 'vue'
5
5
  type NumberLayout = 'default' | 'vertical' | 'horizontal'
6
6
 
7
7
  interface NumberInputProps {
8
- modelValue: number
8
+ modelValue?: number
9
9
  min?: number
10
10
  max?: number
11
11
  step?: number
@@ -14,7 +14,7 @@ interface NumberInputProps {
14
14
  icon?: MaterialIcons
15
15
  label?: string
16
16
  placeholder?: string
17
- disabled: boolean
17
+ disabled?: boolean
18
18
  required?: boolean
19
19
  id?: string
20
20
  helptext?: string
@@ -41,7 +41,7 @@ const {
41
41
 
42
42
  const emit = defineEmits(['update:modelValue'])
43
43
 
44
- let numberValue = $ref(0)
44
+ let numberValue = $ref(modelValue || 0)
45
45
 
46
46
  const btnLayouts: NumberLayout[] = ['horizontal', 'vertical']
47
47
 
@@ -81,7 +81,7 @@ watch(() => numberValue, () => {
81
81
 
82
82
  watch(() => modelValue, (newVal) => {
83
83
  if (newVal !== numberValue) {
84
- numberValue = newVal
84
+ numberValue = newVal || 0
85
85
  }
86
86
  }, { immediate: true })
87
87
  </script>
@@ -97,7 +97,7 @@ watch(() => modelValue, (newVal) => {
97
97
  <label :for="id">
98
98
  {{ label }}
99
99
  <div class="gap-025" :class="{ 'column flex': layout === 'vertical', 'flex': layout === 'horizontal' }">
100
- <Btn v-if="layout && btnLayouts.includes(layout)" icon="add" class="radius" :class="[{ 'bgl-big-ctrl-num-btn': layout === 'vertical' }]" @click="increment" />
100
+ <Btn v-if="layout && btnLayouts.includes(layout)" flat icon="add" class="radius" :class="[{ 'bgl-big-ctrl-num-btn': layout === 'vertical' }]" @click="increment" />
101
101
 
102
102
  <input
103
103
  :id
@@ -131,7 +131,7 @@ watch(() => modelValue, (newVal) => {
131
131
  v-if="icon"
132
132
  :icon
133
133
  />
134
- <Btn v-if="layout && btnLayouts.includes(layout)" icon="remove" class="radius" :class="[{ 'bgl-big-ctrl-num-btn': layout === 'vertical' }]" @click="decrement" />
134
+ <Btn v-if="layout && btnLayouts.includes(layout)" flat icon="remove" class="radius" :class="[{ 'bgl-big-ctrl-num-btn': layout === 'vertical' }]" @click="decrement" />
135
135
 
136
136
  <div v-if="spinner && (!layout || !btnLayouts.includes(layout))" class="flex column spinner">
137
137
  <Btn icon="add" flat thin class="bgl-ctrl-num-btn" @click="increment" />
@@ -1,5 +1,4 @@
1
1
  <script setup lang="ts">
2
- import type { MaterialIcons } from '@bagelink/vue'
3
2
  import type { BglFile, QueueFile } from './upload.types'
4
3
  import { Btn, IMAGE_FORMATS_REGEXP, Icon, useBagel, Card, Image } from '@bagelink/vue'
5
4
  import { watch } from 'vue'
@@ -26,12 +25,12 @@ const props = withDefaults(defineProps<{
26
25
  })
27
26
 
28
27
  const emit = defineEmits(['update:modelValue', 'addFileStart'])
29
-
30
28
  const bagel = useBagel()
31
- const fileBaseUrl = $computed(() => (props.baseURL || bagel.fileBaseUrl || 'https://files.bagel.design/').replace(/\/$/, ''))
32
29
 
33
30
  files.setBaseUrl(bagel.host)
34
31
 
32
+ const fileBaseUrl = $computed(() => (props.baseURL || bagel.fileBaseUrl || 'https://files.bagel.design').replace(/\/$/, ''))
33
+ const pathToUrl = (path_key: string) => `${fileBaseUrl}/${path_key}`
35
34
  const fileQueue = $ref<QueueFile[]>([])
36
35
  let storageFiles = $ref<BglFile[]>([])
37
36
  const pk = $ref<string[]>([props.modelValue].flat().filter(Boolean) as string[])
@@ -47,7 +46,6 @@ watch(() => pk, (value) => {
47
46
  }, { deep: true })
48
47
 
49
48
  const isImage = (str: string) => IMAGE_FORMATS_REGEXP.test(str)
50
- const pathToUrl = (path_key: string) => `${fileBaseUrl}/${path_key}`
51
49
  const fileToUrl = (file: File) => URL.createObjectURL(file)
52
50
 
53
51
  let isDragOver = $ref(false)
@@ -79,7 +77,7 @@ async function flushQueue() {
79
77
  })
80
78
  pk.push(data.path_key)
81
79
  } catch (error) {
82
- console.error(error)
80
+ console.error('error flushing queue', error)
83
81
  }
84
82
  }
85
83
  fileQueue.splice(0, fileQueue.length)
@@ -166,18 +164,25 @@ watch(() => props.dirPath, () => {
166
164
  <div class="txt-gray txt-12 flex">
167
165
  <div class="m-05 flex opacity-7 z-99">
168
166
  <Btn
169
- v-for="action in [
170
- { tooltip: 'Delete', icon: 'delete' as MaterialIcons, onClick: () => removeFile(path_key) },
171
- { tooltip: 'Replace', icon: 'autorenew' as MaterialIcons, onClick: browse },
172
- { tooltip: 'Download', icon: 'download' as MaterialIcons, href: pathToUrl(path_key), download: path_key.split('/').pop() },
173
- ]"
174
- :key="action.tooltip"
175
- v-tooltip="action.tooltip"
167
+ v-tooltip="'Delete'"
176
168
  color="gray"
177
169
  thin
178
- :icon="action.icon"
179
- v-bind="action.href ? { href: action.href, download: action.download, target: '_blank' } : {}"
180
- @click.stop="action.onClick"
170
+ icon="delete"
171
+ @click="removeFile(path_key)"
172
+ />
173
+ <Btn
174
+ v-tooltip="'Replace'"
175
+ color="gray"
176
+ thin
177
+ icon="autorenew"
178
+ @click="browse"
179
+ />
180
+ <Btn
181
+ icon="download"
182
+ flat
183
+ thin
184
+ :href="pathToUrl(path_key)"
185
+ :download="path_key.split('/').pop()"
181
186
  />
182
187
  <p
183
188
  v-lightbox="{ src: pathToUrl(path_key), download: true }"
@@ -222,9 +227,8 @@ watch(() => props.dirPath, () => {
222
227
  >
223
228
  <Image
224
229
  v-if="isImage(path_key)"
230
+ :pathKey="path_key"
225
231
  class="multi-preview"
226
- :src="pathToUrl(path_key)"
227
- alt=""
228
232
  />
229
233
  <Icon v-else icon="description" class="multi-preview" />
230
234
  <p class="m-0">
@@ -235,7 +239,7 @@ watch(() => props.dirPath, () => {
235
239
  flat
236
240
  icon="delete"
237
241
  color="red"
238
- @click.stop="removeFile(path_key)"
242
+ @click="removeFile(path_key)"
239
243
  />
240
244
  </div>
241
245
  <div
@@ -280,25 +284,32 @@ watch(() => props.dirPath, () => {
280
284
  >
281
285
  <div class="position-start m-05 flex opacity-7 z-99 gap-025">
282
286
  <Btn
283
- v-for="action in [
284
- { tooltip: 'Delete', icon: 'delete' as MaterialIcons, onClick: () => removeFile(path_key) },
285
- { tooltip: 'Replace', icon: 'autorenew' as MaterialIcons, onClick: browse },
286
- { tooltip: 'Download', icon: 'download' as MaterialIcons, href: pathToUrl(path_key), download: path_key.split('/').pop() },
287
- ]"
288
- :key="action.tooltip"
289
- v-tooltip="action.tooltip"
287
+ v-tooltip="'Delete'"
288
+ color="gray"
289
+ thin
290
+ icon="delete"
291
+ @click="removeFile(path_key)"
292
+ />
293
+ <Btn
294
+ v-tooltip="'Replace'"
295
+ color="gray"
296
+ thin
297
+ icon="autorenew"
298
+ @click="browse"
299
+ />
300
+ <Btn
301
+ v-tooltip="'Download'"
290
302
  color="gray"
291
303
  thin
292
- :icon="action.icon"
293
- v-bind="action.href ? { href: action.href, download: action.download, target: '_blank' } : {}"
294
- @click.stop="action.onClick"
304
+ icon="download"
305
+ :href="pathToUrl(path_key)"
306
+ :download="path_key.split('/').pop()"
295
307
  />
296
308
  </div>
297
309
  <div v-if="isImage(path_key)" class="h-100">
298
310
  <Image
299
- v-lightbox="{ src: pathToUrl(path_key), download: true }"
300
311
  class="single-preview"
301
- :src="pathToUrl(path_key)"
312
+ :pathKey="path_key"
302
313
  alt=""
303
314
  />
304
315
  </div>
@@ -19,4 +19,4 @@ export { default as TableField } from './TableField.vue'
19
19
  export { default as TelInput } from './TelInput.vue'
20
20
  export { default as TextInput } from './TextInput.vue'
21
21
  export { default as ToggleInput } from './ToggleInput.vue'
22
- export { default as Upload } from './Upload/UploadFile.vue'
22
+ export { default as UploadInput } from './Upload/UploadInput.vue'
@@ -142,7 +142,7 @@ export function numField(
142
142
  options?: NumFieldOptions,
143
143
  ): Field {
144
144
  return {
145
- $el: 'text',
145
+ $el: 'number',
146
146
  class: options?.class,
147
147
  required: options?.required,
148
148
  defaultValue: options?.defaultValue,
@@ -152,12 +152,9 @@ export function numField(
152
152
  placeholder: options?.placeholder,
153
153
  helptext: options?.helptext,
154
154
  attrs: {
155
- type: 'number',
156
- nativeInputAttrs: {
157
- step: options?.step,
158
- min: options?.min,
159
- max: options?.max,
160
- },
155
+ step: options?.step,
156
+ min: options?.min,
157
+ max: options?.max,
161
158
  },
162
159
  }
163
160
  }
@@ -170,6 +167,19 @@ export function frmRow(...children: Field[]) {
170
167
  }
171
168
  }
172
169
 
170
+ interface UploadOptions extends InputOptions {
171
+ multiple?: boolean
172
+ }
173
+
174
+ export function uploadField(id: string, label?: string, options?: UploadOptions) {
175
+ return {
176
+ $el: 'upload',
177
+ id,
178
+ label,
179
+ attrs: options,
180
+ }
181
+ }
182
+
173
183
  export function bglForm(idOrField?: string | Field, ...schema: Field[]) {
174
184
  if (typeof idOrField === 'string') {
175
185
  return {