@bagelink/vue 1.5.32 → 1.6.4

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": "1.5.32",
4
+ "version": "1.6.4",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Neveh Allon",
@@ -21,6 +21,7 @@ const props = withDefaults(
21
21
  fullWidth?: boolean
22
22
  ellipsis?: boolean
23
23
  ripple?: boolean
24
+ tiny?: boolean
24
25
  onClick?: () => void
25
26
  }>(),
26
27
  {
@@ -30,9 +31,9 @@ const props = withDefaults(
30
31
  )
31
32
 
32
33
  const isComponent = $computed(() => {
33
- if (props.to) {return 'router-link'}
34
- if (props.href) {return 'a'}
35
- if (props.onClick) {return 'button'}
34
+ if (props.to) { return 'router-link' }
35
+ if (props.href) { return 'a' }
36
+ if (props.onClick) { return 'button' }
36
37
  return 'div'
37
38
  })
38
39
 
@@ -42,15 +43,15 @@ const isClickable = $computed(() => {
42
43
 
43
44
  const bind = $computed(() => {
44
45
  const obj: { [key: string]: any } = {}
45
- if (props.to) {obj.to = props.to}
46
- else if (props.href) {obj.href = props.href}
47
- if (props.target && (props.to || props.href)) {obj.target = props.target}
46
+ if (props.to) { obj.to = props.to }
47
+ else if (props.href) { obj.href = props.href }
48
+ if (props.target && (props.to || props.href)) { obj.target = props.target }
48
49
 
49
50
  obj.class = {
50
51
  'notClickable': !(props.to || props.onClick),
51
52
  'no-border-list': props.flat,
52
53
  }
53
- if (props.disabled) {obj.disabled = true}
54
+ if (props.disabled) { obj.disabled = true }
54
55
  return obj
55
56
  })
56
57
  </script>
@@ -60,8 +61,9 @@ const bind = $computed(() => {
60
61
  <component
61
62
  :is="isComponent" v-bind="bind" v-ripple="ripple && isClickable"
62
63
  class="flex flex-grow-1 gap-05 list-item" :class="{
63
- 'py-1': !props.thin,
64
+ 'py-1': !props.thin && !props.tiny,
64
65
  'py-05': props.thin,
66
+ 'py-0': props.tiny,
65
67
  'px-1': !props.fullWidth,
66
68
  'px-0': props.fullWidth,
67
69
  }" @click="onClick"
@@ -0,0 +1,79 @@
1
+ <script setup lang="ts" generic="T = unknown">
2
+ import type { Ref, WritableComputedRef } from 'vue'
3
+
4
+ import { Btn } from '@bagelink/vue'
5
+ import { computed, ref, watch } from 'vue'
6
+
7
+ const props = defineProps<{
8
+ label?: string
9
+ helpText?: string
10
+ modelValue: T[]
11
+ allowAdd?: boolean
12
+ allowDelete?: boolean
13
+ }>()
14
+
15
+ const emit = defineEmits(['update:modelValue'])
16
+
17
+ defineSlots<{
18
+ default: (props: { item: WritableComputedRef<T>, index: number }) => unknown
19
+ }>()
20
+
21
+ const items = ref(Array.isArray(props.modelValue) ? [...props.modelValue] : []) as Ref<T[]>
22
+
23
+ watch(
24
+ () => props.modelValue,
25
+ (newVal) => {
26
+ if (Array.isArray(newVal)) {
27
+ items.value = [...newVal]
28
+ }
29
+ }
30
+ )
31
+
32
+ function addItem() {
33
+ items.value.push(undefined as T)
34
+ updateModel()
35
+ }
36
+
37
+ function deleteItem(i: number) {
38
+ items.value.splice(i, 1)
39
+ updateModel()
40
+ }
41
+
42
+ function updateModel() {
43
+ emit('update:modelValue', [...items.value])
44
+ }
45
+
46
+ function onUpdate(value: T, i: number) {
47
+ items.value[i] = value
48
+ updateModel()
49
+ }
50
+
51
+ // Create a writable computed ref for each item
52
+ function getItemRef(i: number) {
53
+ return computed({
54
+ get: () => items.value[i],
55
+ set: (value) => {
56
+ onUpdate(value, i)
57
+ }
58
+ })
59
+ }
60
+ </script>
61
+
62
+ <template>
63
+ <div>
64
+ <label v-if="label" class="txt12 txt-gray-90 mb-025">
65
+ {{ label }}
66
+ </label>
67
+ <p v-if="helpText" class="txt12 txt-gray-50 mb-05">
68
+ {{ helpText }}
69
+ </p>
70
+ <div
71
+ v-for="(_, i) in items" :key="i" class="array-input-row"
72
+ style="display: flex; align-items: center; gap: 0.5rem;"
73
+ >
74
+ <slot :item="getItemRef(i)" :index="i" />
75
+ <Btn v-if="allowDelete" v-tooltip="'Delete'" flat icon="delete" size="xs" @click="deleteItem(i)" />
76
+ </div>
77
+ <Btn v-if="allowAdd" icon="add" size="small" value="Add" @click="addItem" />
78
+ </div>
79
+ </template>
@@ -7,7 +7,7 @@ import { useHighlight } from './useHighlight'
7
7
  const props = defineProps({
8
8
  language: { type: String as PropType<Language>, default: 'html' },
9
9
  readonly: { type: Boolean, default: false },
10
- modelValue: { type: String, default: '' },
10
+ modelValue: { type: [String, Object] as PropType<string | Record<string, any>>, default: '' },
11
11
  autodetect: { type: Boolean, default: true },
12
12
  ignoreIllegals: { type: Boolean, default: true },
13
13
  label: { type: String, default: '' },
@@ -17,13 +17,26 @@ const props = defineProps({
17
17
  })
18
18
 
19
19
  const emit = defineEmits(['update:modelValue'])
20
+
21
+ // Track whether the original modelValue was an object
22
+ const isObjectMode = computed(() => typeof props.modelValue === 'object')
23
+
24
+ // Helper to convert value to string for display
25
+ function valueToString(value: string | Record<string, any>): string {
26
+ if (typeof value === 'string') return value
27
+ if (typeof value === 'object') {
28
+ return JSON.stringify(value, null, 2)
29
+ }
30
+ return ''
31
+ }
32
+
20
33
  // State
21
- const code = ref(props.modelValue || '')
34
+ const code = ref(valueToString(props.modelValue))
22
35
  const editorRef = ref<HTMLDivElement>()
23
36
  const { loaded, loadHighlight, highlightCode, setTheme } = useHighlight(props.theme)
24
37
 
25
38
  const maxHeight = computed(() => {
26
- const h = props.height ?? '240px'
39
+ const h = props.height
27
40
  return h.match(/^\d+$/) ? `${h}px` : h
28
41
  })
29
42
 
@@ -39,11 +52,24 @@ const formattedCode = computed(() => {
39
52
  function handleInput(e: Event) {
40
53
  const target = e.target as HTMLTextAreaElement
41
54
  code.value = target.value
42
- emit('update:modelValue', code.value)
55
+
56
+ // If originally an object, try to parse and emit object
57
+ if (isObjectMode.value) {
58
+ try {
59
+ const parsed = JSON.parse(code.value)
60
+ emit('update:modelValue', parsed)
61
+ } catch {
62
+ // If parsing fails, emit the string as-is (user is still typing)
63
+ // This allows intermediate invalid JSON states while editing
64
+ emit('update:modelValue', code.value)
65
+ }
66
+ } else {
67
+ emit('update:modelValue', code.value)
68
+ }
43
69
  }
44
70
 
45
71
  function handleTab(event: KeyboardEvent) {
46
- if ('Tab' !== event.key) {return}
72
+ if (event.key === 'Tab') { return }
47
73
 
48
74
  event.preventDefault()
49
75
  const target = event.target as HTMLTextAreaElement
@@ -53,11 +79,24 @@ function handleTab(event: KeyboardEvent) {
53
79
  // Add tab or indent selected text
54
80
  const newValue = `${code.value.substring(0, start)} ${code.value.substring(end)}`
55
81
  code.value = newValue
56
- emit('update:modelValue', code.value)
82
+
83
+ // If originally an object, try to parse and emit object
84
+ if (isObjectMode.value) {
85
+ try {
86
+ const parsed = JSON.parse(code.value)
87
+ emit('update:modelValue', parsed)
88
+ } catch {
89
+ // If parsing fails, emit the string as-is
90
+ emit('update:modelValue', code.value)
91
+ }
92
+ } else {
93
+ emit('update:modelValue', code.value)
94
+ }
57
95
 
58
96
  // Move cursor position after the inserted tab
59
97
  setTimeout(() => {
60
- target.selectionStart = target.selectionEnd = start + 2
98
+ target.selectionStart = start + 2
99
+ target.selectionEnd = start + 2
61
100
  }, 0)
62
101
  }
63
102
 
@@ -68,8 +107,9 @@ onMounted(async () => {
68
107
 
69
108
  watch(() => props.theme, (t) => { setTheme(t) })
70
109
  watch(() => props.modelValue, (newVal) => {
71
- if (newVal !== undefined && newVal !== code.value) {
72
- code.value = newVal
110
+ const newCodeStr = valueToString(newVal)
111
+ if (newCodeStr !== code.value) {
112
+ code.value = newCodeStr
73
113
  }
74
114
  }, { immediate: true })
75
115
  </script>
@@ -13,7 +13,7 @@ const props = withDefaults(defineProps<PropTypes>(), {
13
13
 
14
14
  const emit = defineEmits(['update:modelValue'])
15
15
 
16
- const isAsyncSource = (src: OptionsSource): src is (q: string) => Promise<Option[]> => 'function' === typeof src
16
+ const isAsyncSource = (src: OptionsSource): src is (q: string) => Promise<Option[]> => typeof src === 'function'
17
17
 
18
18
  type Primitive = string | number | boolean
19
19
 
@@ -48,8 +48,8 @@ let selected = $ref(false)
48
48
  let open = $ref(false)
49
49
 
50
50
  const selectedLabel = $computed((): string => {
51
- if (0 === selectedItemCount) {return props.placeholder}
52
- if (4 < selectedItemCount) {
51
+ if (selectedItemCount === 0) { return props.placeholder }
52
+ if (selectedItemCount > 4) {
53
53
  const str = selectedItems
54
54
  .slice(0, 4)
55
55
  .map(item => getLabel(item))
@@ -77,30 +77,30 @@ function navigate(direction: 'up' | 'down') {
77
77
  setTimeout(() => { navigate(direction) }, 210)
78
78
  return
79
79
  }
80
- if ('up' === direction) {
81
- highlightedIndex = 0 < highlightedIndex ? highlightedIndex - 1 : results.value.length - 1
82
- } else if ('down' === direction) {
80
+ if (direction === 'up') {
81
+ highlightedIndex = highlightedIndex > 0 ? highlightedIndex - 1 : results.value.length - 1
82
+ } else if (direction === 'down') {
83
83
  highlightedIndex = highlightedIndex < results.value.length - 1 ? highlightedIndex + 1 : 0
84
84
  }
85
85
  setTimeout(() => {
86
86
  const el = selectOptions?.children[highlightedIndex] as HTMLElement
87
- if (el) {el.focus()}
88
- else {highlightedIndex = -1}
87
+ if (el) { el.focus() }
88
+ else { highlightedIndex = -1 }
89
89
  }, 10)
90
90
  }
91
91
 
92
92
  const isSelected = (option: Option) => selectedItems.find(item => getValue(option) === getValue(item)) !== undefined
93
93
 
94
94
  function scrollToSelectedItem() {
95
- if (!selectOptions || 0 === selectedItemCount) {return}
95
+ if (!selectOptions || selectedItemCount === 0) { return }
96
96
 
97
97
  // Find the first selected item in the results
98
98
  const selectedIndex = results.value.findIndex(option => isSelected(option))
99
- if (-1 === selectedIndex) {return}
99
+ if (selectedIndex === -1) { return }
100
100
 
101
101
  // Get the selected option element
102
102
  const selectedElement = selectOptions.children[selectedIndex] as HTMLElement
103
- if (!selectedElement) {return}
103
+ if (!selectedElement) { return }
104
104
 
105
105
  // Scroll the selected item into view
106
106
  selectedElement.scrollIntoView({
@@ -110,22 +110,22 @@ function scrollToSelectedItem() {
110
110
  }
111
111
 
112
112
  function getLabel(option: Option) {
113
- if (null == option) {return ''}
114
- if ('object' === typeof option) {return option.label ?? String((option as any).value ?? '')}
115
- if ('boolean' === typeof option) {return option ? 'Yes' : 'No'}
113
+ if (option == null) { return '' }
114
+ if (typeof option === 'object') { return option.label ?? String((option as any).value ?? '') }
115
+ if (typeof option === 'boolean') { return option ? 'Yes' : 'No' }
116
116
  return String(option)
117
117
  }
118
118
 
119
119
  function getValue(option?: Option): Primitive | undefined {
120
- if (null == option) {return}
121
- if ('object' === typeof option) {return option.value as Primitive}
120
+ if (option == null) { return }
121
+ if (typeof option === 'object') { return option.value as Primitive }
122
122
  return option as Primitive
123
123
  }
124
124
 
125
125
  function focusInput() {
126
126
  open = true
127
127
  setTimeout(() => {
128
- if (searchInput) {searchInput.focus()}
128
+ if (searchInput) { searchInput.focus() }
129
129
  }, 10)
130
130
  }
131
131
 
@@ -134,7 +134,7 @@ function select(option: Option) {
134
134
  const existingIndex = selectedItems.findIndex(
135
135
  item => getValue(item) === getValue(option),
136
136
  )
137
- if (-1 < existingIndex) {
137
+ if (existingIndex > -1) {
138
138
  selectedItems.splice(existingIndex, 1)
139
139
  }
140
140
  else if (props.multiselect) {
@@ -148,7 +148,7 @@ function select(option: Option) {
148
148
 
149
149
  // Move focus away from popper content before it gets aria-hidden
150
150
  const active = document.activeElement as HTMLElement | null
151
- if (active && selectOptions?.contains(active)) {active.blur()}
151
+ if (active && selectOptions?.contains(active)) { active.blur() }
152
152
 
153
153
  if (!props.multiselect) {
154
154
  open = false
@@ -169,7 +169,7 @@ function emitUpdate() {
169
169
  }
170
170
 
171
171
  function compareArrays(arr1: Option[], arr2: Option[]) {
172
- if (arr1.length !== arr2.length) {return false}
172
+ if (arr1.length !== arr2.length) { return false }
173
173
  const s1 = arr1.map(getValue).filter(Boolean).map(String).sort()
174
174
  const s2 = arr2.map(getValue).filter(Boolean).map(String).sort()
175
175
  return s1.every((v, i) => v === s2[i])
@@ -182,7 +182,7 @@ watch(
182
182
  const newOption = Array.isArray(props.options)
183
183
  ? (props.options.find(o => getValue(o) === newVal) ?? newVal)
184
184
  : newVal
185
- if (newOption && !isSelected(newOption)) {selectedItems = [newOption]}
185
+ if (newOption && !isSelected(newOption)) { selectedItems = [newOption] }
186
186
  } else {
187
187
  const newData = [newVal].flat()
188
188
  const isSame = compareArrays(newData, selectedItems)
@@ -206,13 +206,13 @@ watch(
206
206
  () => props.options,
207
207
  () => {
208
208
  const opts = props.options
209
- if (!Array.isArray(opts)) {return}
209
+ if (!Array.isArray(opts)) { return }
210
210
  selectedItems.forEach((option, i) => {
211
211
  const exists = opts.find(
212
212
  (o: Option) => getValue(o) === getValue(option),
213
213
  )
214
- if (exists === undefined) {selectedItems.splice(i, 1)}
215
- else {selectedItems.splice(i, 1, exists)}
214
+ if (exists === undefined) { selectedItems.splice(i, 1) }
215
+ else { selectedItems.splice(i, 1, exists) }
216
216
  })
217
217
  // const original = JSON.stringify(props.options.map(getValue));
218
218
  // const newSelection = JSON.stringify(selectedItems.map(getValue));
@@ -225,11 +225,11 @@ watch(
225
225
  watch(
226
226
  () => results.value,
227
227
  (newResults) => {
228
- if (isAsyncSource(props.options) && 0 < newResults.length) {
228
+ if (isAsyncSource(props.options) && newResults.length > 0) {
229
229
  selectedItems.forEach((option, i) => {
230
230
  const optionValue = getValue(option)
231
231
  // If the selected item is just a primitive value (no label), find the full option
232
- if ('object' !== typeof option || !option.label) {
232
+ if (typeof option !== 'object' || !option.label) {
233
233
  const fullOption = newResults.find(
234
234
  (o: Option) => getValue(o) === optionValue,
235
235
  )
@@ -250,7 +250,7 @@ onMounted(() => {
250
250
  (o: Option) => getValue(o) === getValue(props.defaultValue),
251
251
  )
252
252
 
253
- if (defaultOption === undefined) {return}
253
+ if (defaultOption === undefined) { return }
254
254
 
255
255
  selectedItems = [defaultOption]
256
256
  }
@@ -411,7 +411,7 @@ onMounted(() => {
411
411
  .v-popper--theme-dropdown .v-popper__inner {
412
412
  border: none;
413
413
  /* background: transparent; if anyone is changing this please talk to me first*/
414
- border-radius: var(--card-border-radius);
414
+ border-radius: var(--popper-border-radius);
415
415
  color: var(--dropdown-color);
416
416
  }
417
417
  </style>
@@ -1,3 +1,4 @@
1
+ export { default as ArrayInput } from './ArrayInput.vue'
1
2
  export { default as Checkbox } from './Checkbox.vue'
2
3
  export { default as CheckInput } from './CheckInput.vue'
3
4
  export { default as CodeEditor } from './CodeEditor/Index.vue'
@@ -1391,6 +1391,22 @@
1391
1391
  white-space: nowrap;
1392
1392
  }
1393
1393
 
1394
+ .white-space-break-spaces {
1395
+ white-space: break-spaces;
1396
+ }
1397
+
1398
+ .white-space-pre {
1399
+ white-space: pre;
1400
+ }
1401
+
1402
+ .white-space-pre-line {
1403
+ white-space: pre-line;
1404
+ }
1405
+
1406
+ .white-space-pre-wrap {
1407
+ white-space: pre-wrap;
1408
+ }
1409
+
1394
1410
  .notranslate {
1395
1411
  translate: none !important;
1396
1412
  -webkit-translate: none !important;
@@ -2791,4 +2807,24 @@
2791
2807
  .m_capitalize {
2792
2808
  text-transform: capitalize;
2793
2809
  }
2810
+
2811
+ .m_white-space {
2812
+ white-space: nowrap;
2813
+ }
2814
+
2815
+ .m_white-space-break-spaces {
2816
+ white-space: break-spaces;
2817
+ }
2818
+
2819
+ .m_white-space-pre {
2820
+ white-space: pre;
2821
+ }
2822
+
2823
+ .m_white-space-pre-line {
2824
+ white-space: pre-line;
2825
+ }
2826
+
2827
+ .m_white-space-pre-wrap {
2828
+ white-space: pre-wrap;
2829
+ }
2794
2830
  }
@@ -24,6 +24,7 @@
24
24
  --bgl-box-bg: var(--bgl-white);
25
25
  --bgl-popup-bg: var(--bgl-white);
26
26
  --bgl-popup-text: var(--bgl-black);
27
+ --popper-border-radius: var(--card-border-radius);
27
28
  --bgl-text-color: var(--bgl-black);
28
29
  --bgl-light-text: var(--bgl-white);
29
30
  --bgl-richtext-color: var(--bgl-white);