@bagelink/vue 1.12.57 → 1.12.67

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.12.57",
4
+ "version": "1.12.67",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
@@ -4,7 +4,7 @@ import type { SetupContext } from 'vue'
4
4
  import { Icon, Loading, useModal, useI18n, resolveI18n } from '@bagelink/vue'
5
5
  import { useSlots, ref, onMounted, onUnmounted, computed } from 'vue'
6
6
  import { RouterLink } from 'vue-router'
7
-
7
+ defineOptions({ name: 'BglBtn' })
8
8
  const props = withDefaults(
9
9
  defineProps<{
10
10
  disabled?: boolean
@@ -23,7 +23,7 @@ const props = withDefaults(
23
23
  loading?: boolean
24
24
  role?: string
25
25
  value?: TranslatableString
26
- to?: string
26
+ to?: string | Record<string, any>
27
27
  href?: string
28
28
  round?: boolean
29
29
  is?: string
@@ -58,15 +58,16 @@ defineExpose({ contentWindow })
58
58
  ref="iframe"
59
59
  :src
60
60
  :title
61
- frameborder="none"
62
- :width
63
- :height
61
+ frameborder="0"
62
+ :width="width ?? '100%'"
63
+ :height="height ?? '100%'"
64
64
  allowpaymentrequest="true"
65
65
  :marginwidth
66
66
  :marginheight
67
67
  :csp
68
68
  :scrolling
69
69
  :srcset
70
+ style="display:block; border:none;"
70
71
  @load="$event => emit('load', $event)"
71
72
  />
72
73
  </template>
@@ -43,8 +43,13 @@ defineProps<{ items: NavLink[] }>()
43
43
  </template>
44
44
 
45
45
  <style scoped>
46
- .bgl_btn.router-link-active {
46
+ /* .bgl_btn.router-link-active {
47
47
  background-color: var(--bgl-gray-40);
48
+ } */
49
+
50
+ .bglTopMenu .router-link-active {
51
+ background: var(--bgl-primary) !important;
52
+ color: var(--bgl-white) !important;
48
53
  }
49
54
  </style>
50
55
 
@@ -714,7 +714,7 @@ onUnmounted(() => {
714
714
  backgroundColor: event.color || 'var(--bgl-primary)',
715
715
  }" @mousedown.stop @click.stop="handleEventSelection(event, $event)"
716
716
  >
717
- <div class="overflow-hidden color-white p-025 txt12 h-100p">
717
+ <div class="overflow-hidden color-white p-025 txt12 h-100p flex gap-1 flex-wrap">
718
718
  <div class="white-space ellipsis-1">
719
719
  {{ event.title }}
720
720
  </div>
@@ -31,6 +31,8 @@ const props = withDefaults(defineProps<{
31
31
  format?: FormatType
32
32
  clearable?: boolean
33
33
  required?: boolean
34
+ label?: string
35
+ helptext?: string
34
36
  }>(), {
35
37
  clearable: true,
36
38
  })
@@ -218,6 +220,8 @@ defineExpose({
218
220
  :class="{ 'bg-transparent': disabled }"
219
221
  @touchmove.prevent
220
222
  >
223
+ <label v-if="label" class="label">{{ label }}</label>
224
+ <p v-if="helptext" class="helptext">{{ helptext }}</p>
221
225
  <Btn
222
226
  v-if="clearable && !disabled"
223
227
  flat
@@ -258,6 +262,19 @@ defineExpose({
258
262
  border-radius: var(--input-border-radius);
259
263
  }
260
264
 
265
+ .bgl_input.signature-pad > .label {
266
+ display: block;
267
+ font-size: 0.8rem;
268
+ padding: 0.25rem 0.5rem 0;
269
+ }
270
+
271
+ .bgl_input.signature-pad > .helptext {
272
+ font-size: 0.75rem;
273
+ padding: 0.15rem 0.5rem 0.25rem;
274
+ margin: 0;
275
+ line-height: 1.4;
276
+ }
277
+
261
278
  .bgl_input.signature-pad .canvas[disabled] {
262
279
  background: var(--input-disabled-bg);
263
280
  border: 1px solid var(--bgl-gray);
@@ -5,7 +5,7 @@ import { inject, computed } from 'vue'
5
5
  interface Props {
6
6
  title?: string
7
7
  showMenuButton?: boolean
8
- backTo?: string
8
+ backTo?: string | Record<string, any>
9
9
  border?: boolean
10
10
  }
11
11
 
@@ -22,6 +22,7 @@ const props = defineProps<{
22
22
  fullWidthMobile?: boolean
23
23
  alignTxt?: 'center' | 'start' | 'end'
24
24
  alignTxtMobile?: 'center' | 'start' | 'end'
25
+ outline?: boolean
25
26
  }>()
26
27
 
27
28
  const emit = defineEmits(['update:modelValue'])
@@ -34,21 +35,40 @@ currentTab.value
34
35
  const tabsWrap = ref<HTMLElement | undefined>(undefined)
35
36
  const tabEls = ref<HTMLElement[]>([])
36
37
 
38
+ let resizeObserver: ResizeObserver | undefined
39
+
40
+ /** Position relative to tabs wrap content box (works after breakpoint / font / scroll changes). */
41
+ function measureActiveTabIndicator(wrap: HTMLElement, activeTab: HTMLElement) {
42
+ const wrapRect = wrap.getBoundingClientRect()
43
+ const tabRect = activeTab.getBoundingClientRect()
44
+ const left = tabRect.left - wrapRect.left + wrap.scrollLeft
45
+ const width = tabRect.width
46
+ if (width > 0) {
47
+ wrap.style.setProperty('--indicator-left', `${left}px`)
48
+ wrap.style.setProperty('--indicator-width', `${width}px`)
49
+ wrap.style.setProperty('--indicator-opacity', '1')
50
+ }
51
+ }
52
+
37
53
  function updateIndicator() {
38
54
  nextTick(() => {
39
- if (!tabsWrap.value) { return }
40
- const activeTab = tabEls.value.find(tab => tab.classList.contains('active'))
41
- if (activeTab) {
42
- // Wait a bit more to ensure CSS is fully loaded and elements are properly sized
43
- setTimeout(() => {
44
- const { offsetLeft, offsetWidth } = activeTab
45
- if (offsetLeft >= 0 && offsetWidth > 0) { // Ensure valid measurements
46
- tabsWrap.value?.style.setProperty('--indicator-left', `${offsetLeft}px`)
47
- tabsWrap.value?.style.setProperty('--indicator-width', `${offsetWidth}px`)
48
- tabsWrap.value?.style.setProperty('--indicator-opacity', '1')
49
- }
50
- }, 10)
55
+ const wrap = tabsWrap.value
56
+ if (!wrap) { return }
57
+
58
+ const apply = () => {
59
+ tabEls.value = Array.from(wrap.querySelectorAll<HTMLElement>('.bgl_tab'))
60
+ const activeTab = tabEls.value.find(tab => tab.classList.contains('active'))
61
+ if (!activeTab) {
62
+ wrap.style.setProperty('--indicator-opacity', '0')
63
+ return
64
+ }
65
+ measureActiveTabIndicator(wrap, activeTab)
51
66
  }
67
+
68
+ // Double rAF: layout + media-query / font metrics often settle after one frame (e.g. mobile ↔ desktop).
69
+ requestAnimationFrame(() => {
70
+ requestAnimationFrame(apply)
71
+ })
52
72
  })
53
73
  }
54
74
 
@@ -80,32 +100,51 @@ watch(
80
100
  onMounted(() => {
81
101
  tabEls.value = Array.from(tabsWrap.value?.querySelectorAll('.bgl_tab') || [])
82
102
 
83
- // Wait for the next frame to ensure all CSS and layout calculations are done
84
103
  requestAnimationFrame(() => {
85
104
  updateIndicator()
86
105
  })
87
106
 
88
107
  window.addEventListener('resize', updateIndicator)
108
+ tabsWrap.value?.addEventListener('scroll', updateIndicator, { passive: true })
109
+
110
+ if (typeof ResizeObserver !== 'undefined' && tabsWrap.value) {
111
+ resizeObserver = new ResizeObserver(() => {
112
+ updateIndicator()
113
+ })
114
+ resizeObserver.observe(tabsWrap.value)
115
+ }
116
+
117
+ if (typeof window !== 'undefined' && window.visualViewport) {
118
+ // Mobile browser chrome / zoom — window "resize" alone can miss this.
119
+ window.visualViewport.addEventListener('resize', updateIndicator)
120
+ }
89
121
  })
90
122
 
91
123
  watch(
92
- () => tabEls.value,
124
+ () => props.tabs,
93
125
  () => {
94
126
  nextTick(() => { updateIndicator() })
95
127
  },
96
128
  { deep: true }
97
129
  )
130
+
98
131
  onBeforeUnmount(() => {
99
132
  window.removeEventListener('resize', updateIndicator)
133
+ tabsWrap.value?.removeEventListener('scroll', updateIndicator)
134
+ resizeObserver?.disconnect()
135
+ resizeObserver = undefined
136
+ if (typeof window !== 'undefined' && window.visualViewport) {
137
+ window.visualViewport.removeEventListener('resize', updateIndicator)
138
+ }
100
139
  })
101
140
  </script>
102
141
 
103
142
  <template>
104
- <div ref="tabsWrap" class="grid auto-flow-columns relative fit-content bgl_tabs_wrap overflow-hidden" :class="{ 'bgl_flat-tabs': flat, 'bgl_vertical-tabs': vertical }">
143
+ <div ref="tabsWrap" class="grid auto-flow-columns relative fit-content bgl_tabs_wrap overflow-hidden" :class="{ 'bgl_flat-tabs': flat, 'bgl_vertical-tabs': vertical, 'outline': outline }">
105
144
  <slot name="tabs" v-bind="{ selectTab, isActive, tabLabel, tabs: tabEls }">
106
145
  <button v-for="(tab, i) in props.tabs" :key="i" type="button" :class="[
107
146
  { active: isActive(tab) },
108
- {
147
+ {
109
148
  'bgl_tab-thin': thin,
110
149
  'bgl_tab-xs': size === 'xs' || size === 'extra-small',
111
150
  'bgl_tab-s': size === 's' || size === 'small',
@@ -399,12 +399,18 @@ dialog.dialog-right[style*="--dialog-width: 100%"] {
399
399
  width: calc(100vw - 2rem);
400
400
  }
401
401
 
402
+ dialog.dialog-left .grid-dialog,
403
+ dialog.dialog-right .grid-dialog {
404
+ height: 100%
405
+ }
406
+
402
407
  /* Mobile adjustments */
403
408
  @media screen and (max-width: 910px) {
404
409
 
405
410
  dialog.dialog-left .grid-dialog,
406
411
  dialog.dialog-right .grid-dialog {
407
412
  max-height: 100vh;
413
+ height: unset !important;
408
414
  }
409
415
 
410
416
  dialog.dialog-left,
@@ -11,6 +11,7 @@ import NumberInput from '../components/form/inputs/NumberInput.vue'
11
11
  import PasswordInput from '../components/form/inputs/PasswordInput.vue'
12
12
  import RadioGroup from '../components/form/inputs/RadioGroup.vue'
13
13
  import RichText from '../components/form/inputs/RichText/index.vue'
14
+ import SelectBtn from '../components/form/inputs/SelectBtn.vue'
14
15
  import SelectInput from '../components/form/inputs/SelectInput.vue'
15
16
  import TelInput from '../components/form/inputs/TelInput.vue'
16
17
  import TextInput from '../components/form/inputs/TextInput.vue'
@@ -194,7 +195,13 @@ function generateLabel(key: string, field: FieldBuilder): string {
194
195
  .join(' ')
195
196
  }
196
197
 
197
- function getFieldComponent(type: string) {
198
+ function getFieldComponent(fieldOrType: FieldBuilder | string) {
199
+ // display: 'btn' on select/multiselect renders as SelectBtn
200
+ if (typeof fieldOrType !== 'string' && (fieldOrType._type === 'select' || fieldOrType._type === 'multiselect') && fieldOrType._config.display === 'btn') {
201
+ return SelectBtn
202
+ }
203
+
204
+ const type = typeof fieldOrType === 'string' ? fieldOrType : fieldOrType._type
198
205
  const componentMap: Record<string, any> = {
199
206
  text: TextInput,
200
207
  email: EmailInput,
@@ -435,7 +442,7 @@ defineExpose({
435
442
  </template>
436
443
 
437
444
  <!-- Default field rendering -->
438
- <component :is="getFieldComponent(field._type)" v-else v-bind="getFieldProps(field, key)" />
445
+ <component :is="getFieldComponent(field)" v-else v-bind="getFieldProps(field, key)" />
439
446
  </slot>
440
447
  </div>
441
448
 
@@ -326,8 +326,8 @@ export const $ = {
326
326
 
327
327
  select<T extends string = string>(
328
328
  optionsOrLabel: SelectOptions<T> | string,
329
- labelOrOptionsOrConfig?: string | SelectOptions<T> | (BaseFieldConfig & { searchable?: boolean }),
330
- config?: BaseFieldConfig & { searchable?: boolean }
329
+ labelOrOptionsOrConfig?: string | SelectOptions<T> | (BaseFieldConfig & { searchable?: boolean, display?: 'btn', thin?: boolean, outline?: boolean }),
330
+ config?: BaseFieldConfig & { searchable?: boolean, display?: 'btn', thin?: boolean, outline?: boolean }
331
331
  ): FieldBuilder<T> {
332
332
  // Label-first: $.select('Country', ['USA', 'Canada'])
333
333
  if (typeof optionsOrLabel === 'string') {
@@ -341,8 +341,8 @@ export const $ = {
341
341
 
342
342
  multiselect<T extends string = string>(
343
343
  optionsOrLabel: SelectOptions<T> | string,
344
- labelOrOptionsOrConfig?: string | SelectOptions<T> | (BaseFieldConfig & { searchable?: boolean }),
345
- config?: BaseFieldConfig & { searchable?: boolean }
344
+ labelOrOptionsOrConfig?: string | SelectOptions<T> | (BaseFieldConfig & { searchable?: boolean, display?: 'btn', thin?: boolean, outline?: boolean }),
345
+ config?: BaseFieldConfig & { searchable?: boolean, display?: 'btn', thin?: boolean, outline?: boolean }
346
346
  ): FieldBuilder<T[]> {
347
347
  // Label-first: $.multiselect('Tags', ['tech', 'news'])
348
348
  if (typeof optionsOrLabel === 'string') {