@bagelink/vue 1.12.71 → 1.12.74

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.
Files changed (81) hide show
  1. package/dist/components/Dropdown.vue.d.ts.map +1 -1
  2. package/dist/components/FilterQuery.types.d.ts +19 -0
  3. package/dist/components/FilterQuery.types.d.ts.map +1 -0
  4. package/dist/components/FilterQuery.vue.d.ts.map +1 -1
  5. package/dist/components/analytics/PieChart.vue.d.ts.map +1 -1
  6. package/dist/components/form/inputs/ArrayInput.vue.d.ts.map +1 -1
  7. package/dist/components/form/inputs/CheckInput.vue.d.ts.map +1 -1
  8. package/dist/components/form/inputs/CodeEditor/Index.vue.d.ts.map +1 -1
  9. package/dist/components/form/inputs/ColorInput.vue.d.ts.map +1 -1
  10. package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
  11. package/dist/components/form/inputs/EmailInput.vue.d.ts.map +1 -1
  12. package/dist/components/form/inputs/JSONInput.vue.d.ts.map +1 -1
  13. package/dist/components/form/inputs/MarkdownEditor.vue.d.ts.map +1 -1
  14. package/dist/components/form/inputs/NumberInput.vue.d.ts.map +1 -1
  15. package/dist/components/form/inputs/OTP.vue.d.ts.map +1 -1
  16. package/dist/components/form/inputs/PasswordInput.vue.d.ts.map +1 -1
  17. package/dist/components/form/inputs/RadioGroup.vue.d.ts.map +1 -1
  18. package/dist/components/form/inputs/RangeInput.vue.d.ts.map +1 -1
  19. package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
  20. package/dist/components/form/inputs/SelectBtn.vue.d.ts.map +1 -1
  21. package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
  22. package/dist/components/form/inputs/SignaturePad.vue.d.ts.map +1 -1
  23. package/dist/components/form/inputs/TableField.vue.d.ts.map +1 -1
  24. package/dist/components/form/inputs/TelInput.vue.d.ts.map +1 -1
  25. package/dist/components/form/inputs/TextInput.vue.d.ts.map +1 -1
  26. package/dist/components/form/inputs/ToggleInput.vue.d.ts.map +1 -1
  27. package/dist/components/form/inputs/Upload/UploadInput.vue.d.ts.map +1 -1
  28. package/dist/components/form/inputs/bagelInputShell.d.ts +21 -0
  29. package/dist/components/form/inputs/bagelInputShell.d.ts.map +1 -0
  30. package/dist/components/form/inputs/index.d.ts.map +1 -1
  31. package/dist/components/index.d.ts.map +1 -1
  32. package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -1
  33. package/dist/dialog/Dialog.vue.d.ts.map +1 -1
  34. package/dist/i18n/index.d.ts.map +1 -1
  35. package/dist/index.cjs +135 -135
  36. package/dist/index.mjs +16026 -15661
  37. package/dist/style.css +1 -1
  38. package/dist/types/BagelForm.d.ts.map +1 -1
  39. package/dist/utils/BagelFormUtils.d.ts.map +1 -1
  40. package/package.json +1 -1
  41. package/src/components/Dropdown.vue +2 -1
  42. package/src/components/FilterQuery.types.ts +21 -0
  43. package/src/components/FilterQuery.vue +112 -15
  44. package/src/components/form/inputs/ArrayInput.vue +10 -2
  45. package/src/components/form/inputs/CheckInput.vue +28 -9
  46. package/src/components/form/inputs/CodeEditor/Index.vue +15 -2
  47. package/src/components/form/inputs/ColorInput.vue +77 -9
  48. package/src/components/form/inputs/DateInput.vue +10 -3
  49. package/src/components/form/inputs/EmailInput.vue +90 -54
  50. package/src/components/form/inputs/JSONInput.vue +12 -4
  51. package/src/components/form/inputs/MarkdownEditor.vue +12 -4
  52. package/src/components/form/inputs/NumberInput.vue +154 -89
  53. package/src/components/form/inputs/OTP.vue +46 -7
  54. package/src/components/form/inputs/PasswordInput.vue +32 -21
  55. package/src/components/form/inputs/RadioGroup.vue +18 -7
  56. package/src/components/form/inputs/RangeInput.vue +23 -7
  57. package/src/components/form/inputs/RichText/index.vue +21 -12
  58. package/src/components/form/inputs/SelectBtn.vue +34 -6
  59. package/src/components/form/inputs/SelectInput.vue +19 -25
  60. package/src/components/form/inputs/SignaturePad.vue +39 -14
  61. package/src/components/form/inputs/TableField.vue +6 -2
  62. package/src/components/form/inputs/TelInput.vue +52 -4
  63. package/src/components/form/inputs/TextInput.vue +23 -31
  64. package/src/components/form/inputs/ToggleInput.vue +27 -4
  65. package/src/components/form/inputs/Upload/UploadInput.vue +44 -9
  66. package/src/components/form/inputs/Upload/upload.css +15 -0
  67. package/src/components/form/inputs/bagelInputShell.ts +43 -0
  68. package/src/components/form/inputs/index.ts +1 -0
  69. package/src/components/index.ts +1 -0
  70. package/src/dialog/Dialog.vue +12 -1
  71. package/src/i18n/locales/en.json +4 -1
  72. package/src/i18n/locales/es.json +4 -1
  73. package/src/i18n/locales/fr.json +4 -1
  74. package/src/i18n/locales/he.json +4 -1
  75. package/src/i18n/locales/it.json +4 -1
  76. package/src/i18n/locales/ru.json +4 -1
  77. package/src/styles/input-variants.css +12 -13
  78. package/src/styles/inputs.css +134 -15
  79. package/src/styles/text.css +10 -10
  80. package/src/types/BagelForm.ts +11 -1
  81. package/src/utils/BagelFormUtils.ts +1 -0
@@ -1,8 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  import { resolveI18n } from '@bagelink/vue'
3
3
  import { computed, ref, watch } from 'vue'
4
+ import type { BagelInputShellProps } from './bagelInputShell'
4
5
 
5
- export interface RangeInputProps {
6
+ export interface RangeInputProps extends BagelInputShellProps {
6
7
  modelValue: number | [number, number]
7
8
  min?: number
8
9
  max?: number
@@ -14,6 +15,10 @@ export interface RangeInputProps {
14
15
  rtl?: boolean
15
16
  multiRange?: boolean
16
17
  formatValue?: (value: number) => string
18
+ /** צבע הרקע של הפס (track) */
19
+ trackColor?: string
20
+ /** צבע הפס הפעיל והעיגול — ברירת מחדל: labelActiveColor או primary */
21
+ activeColor?: string
17
22
  }
18
23
 
19
24
  const props = defineProps<RangeInputProps>()
@@ -72,7 +77,15 @@ const displayTo = computed(() => formatValue(validTo.value))
72
77
  </script>
73
78
 
74
79
  <template>
75
- <div :dir="rtl ? 'rtl' : 'ltr'">
80
+ <div
81
+ :dir="rtl ? 'rtl' : 'ltr'"
82
+ :style="[
83
+ minWidth ? { minWidth } : {},
84
+ maxWidth ? { maxWidth } : {},
85
+ activeColor ? { '--bgl-range-thumb-color': activeColor } : {},
86
+ labelActiveColor ? { '--bgl-input-label-active-color': labelActiveColor } : {},
87
+ ]"
88
+ >
76
89
  <label v-if="props.label" class="label">{{ resolveI18n(props.label) }}</label>
77
90
  <div class="range-slider relative w-100">
78
91
  <input
@@ -101,10 +114,13 @@ const displayTo = computed(() => formatValue(validTo.value))
101
114
  aria-label="Maximum value"
102
115
  @input="(e) => handleInput(+(e.target as HTMLInputElement).value, false)"
103
116
  >
104
- <div class="track absolute pointer-events-none overflow-hidden round">
117
+ <div
118
+ class="track absolute pointer-events-none overflow-hidden round"
119
+ :style="trackColor ? { background: trackColor } : {}"
120
+ >
105
121
  <div
106
- class="active-range absolute h-100 pointer-events-none bg-primary"
107
- :style="rangeStyle"
122
+ class="active-range absolute h-100 pointer-events-none"
123
+ :style="{ ...rangeStyle, background: activeColor || 'var(--bgl-input-label-active-color, var(--bgl-primary))' }"
108
124
  />
109
125
  </div>
110
126
  <p
@@ -190,13 +206,13 @@ input[type="range"]::-webkit-slider-thumb {
190
206
  }
191
207
 
192
208
  input[type="range"]::-webkit-slider-thumb:hover {
193
- box-shadow: 0 0 0 calc(var(--bgl-range-thumb-size) / 2) var(--bgl-primary-tint);
209
+ box-shadow: 0 0 0 calc(var(--bgl-range-thumb-size) / 2) color-mix(in srgb, var(--bgl-input-label-active-color, var(--bgl-primary)) 18%, transparent);
194
210
  filter: brightness(90%);
195
211
  }
196
212
 
197
213
  input[type="range"]:focus::-webkit-slider-thumb {
198
214
  outline: none;
199
- box-shadow: 0 0 0 calc(var(--bgl-range-thumb-size) / 2.2) var(--bgl-primary-tint);
215
+ box-shadow: 0 0 0 calc(var(--bgl-range-thumb-size) / 2.2) color-mix(in srgb, var(--bgl-input-label-active-color, var(--bgl-primary)) 18%, transparent);
200
216
  }
201
217
 
202
218
  input[type="range"]:active::-webkit-slider-thumb {
@@ -1,3 +1,4 @@
1
+ <!-- eslint-disable vue/multi-word-component-names -->
1
2
  <script setup lang="ts">
2
3
  import type { ToolbarConfig } from './richTextTypes'
3
4
  import { CodeEditor, copyText, Btn, Modal, BglVideo, Icon, Card, ColorInput, pathKeyToURL } from '@bagelink/vue'
@@ -11,6 +12,8 @@ import EditorToolbar from './components/EditorToolbar.vue'
11
12
  import { useCommands } from './composables/useCommands'
12
13
  import { useEditor } from './composables/useEditor'
13
14
  import { useEditorKeyboard } from './composables/useEditorKeyboard'
15
+ import type { BagelInputShellProps } from '../bagelInputShell'
16
+ import { useBagelInputShell } from '../bagelInputShell'
14
17
 
15
18
  // Disable automatic inheritance of non-prop attributes
16
19
  defineOptions({
@@ -44,10 +47,13 @@ const props = defineProps<{
44
47
  textColor?: string
45
48
  // Font size
46
49
  fontSize?: number | string
47
- }>()
50
+ } & BagelInputShellProps>()
48
51
 
49
52
  const emit = defineEmits(['update:modelValue'])
50
53
 
54
+ const { shellClass, shellStyle } = useBagelInputShell(props)
55
+ const hasEditorValue = computed(() => Boolean(props.modelValue?.trim()))
56
+
51
57
  const attrs = useAttrs()
52
58
 
53
59
  const iframe = ref<HTMLIFrameElement>()
@@ -415,20 +421,20 @@ function showInlineToolbarForSelection() {
415
421
 
416
422
  const selection = doc.getSelection()
417
423
  if (!selection || selection.rangeCount === 0) {
418
- hideInlineToolbar()
424
+ closeInlineToolbar()
419
425
  return
420
426
  }
421
427
 
422
428
  const range = selection.getRangeAt(0)
423
429
  if (range.collapsed) {
424
- hideInlineToolbar()
430
+ closeInlineToolbar()
425
431
  return
426
432
  }
427
433
 
428
434
  // Get the selected text
429
435
  const selectedText = selection.toString().trim()
430
436
  if (!selectedText) {
431
- hideInlineToolbar()
437
+ closeInlineToolbar()
432
438
  return
433
439
  }
434
440
 
@@ -461,7 +467,7 @@ function showInlineToolbarForSelection() {
461
467
  }
462
468
 
463
469
  // Function to hide inline toolbar
464
- function hideInlineToolbar() {
470
+ function closeInlineToolbar() {
465
471
  showInlineToolbar.value = false
466
472
  inlineToolbarSelection.value = null
467
473
  }
@@ -495,7 +501,7 @@ function runInlineAction(actionName: string) {
495
501
  if (!newRange.collapsed) {
496
502
  ; (inlineToolbarSelection.value as any)._storedRange = newRange.cloneRange()
497
503
  } else {
498
- hideInlineToolbar()
504
+ closeInlineToolbar()
499
505
  }
500
506
  }
501
507
  }, 50)
@@ -2103,7 +2109,7 @@ onUnmounted(() => {
2103
2109
  function setupTableEditButtons(doc: Document) {
2104
2110
  // Simple function to add edit buttons to all tables
2105
2111
  function addEditButtonsToTables() {
2106
- const tables = doc.querySelectorAll('table:not([data-edit-button-added])') as NodeListOf<HTMLTableElement>
2112
+ const tables = doc.querySelectorAll('table:not([data-edit-button-added])')
2107
2113
 
2108
2114
  tables.forEach((table, index) => {
2109
2115
  // Create edit button as a span element instead of button
@@ -2418,8 +2424,8 @@ function setupAutoWrapping(doc: Document) {
2418
2424
  const textAfterCursor = range.startContainer.textContent?.substring(range.startOffset) || ''
2419
2425
 
2420
2426
  // Debug logging to understand the issue
2421
- if (props.debug) {
2422
- }
2427
+
2428
+ if (props.debug) { /* reserved for future debug output */ }
2423
2429
 
2424
2430
  // Create new paragraph with proper direction
2425
2431
  const newParagraph = doc.createElement('p')
@@ -2937,7 +2943,7 @@ async function initEditor() {
2937
2943
  setTimeout(() => {
2938
2944
  const selection = doc.getSelection()
2939
2945
  if (!selection || selection.rangeCount === 0 || selection.getRangeAt(0).collapsed) {
2940
- hideInlineToolbar()
2946
+ closeInlineToolbar()
2941
2947
  }
2942
2948
  }, 10)
2943
2949
  })
@@ -2948,7 +2954,7 @@ async function initEditor() {
2948
2954
  setTimeout(() => {
2949
2955
  const selection = doc.getSelection()
2950
2956
  if (!selection || selection.rangeCount === 0 || selection.getRangeAt(0).collapsed) {
2951
- hideInlineToolbar()
2957
+ closeInlineToolbar()
2952
2958
  }
2953
2959
  }, 10)
2954
2960
  }
@@ -3190,7 +3196,10 @@ const imgTransform = computed({
3190
3196
  </script>
3191
3197
 
3192
3198
  <template>
3193
- <div class="bagel-input" v-bind="attrs">
3199
+ <div
3200
+ class="bagel-input" :class="[shellClass, { 'has-value': hasEditorValue }]" :style="shellStyle"
3201
+ v-bind="attrs"
3202
+ >
3194
3203
  <label v-if="label">{{ label }}</label>
3195
3204
 
3196
3205
  <div
@@ -1,7 +1,10 @@
1
1
  <script setup lang="ts">
2
2
  import type { Option } from '@bagelink/vue'
3
3
  import { Btn, Icon, resolveI18n } from '@bagelink/vue'
4
+ import { computed } from 'vue'
4
5
  import { getOptionIcon, getOptionLabel, getOptionValue } from '../../../utils/options'
6
+ import type { BagelInputShellProps } from './bagelInputShell'
7
+ import { useBagelInputShell } from './bagelInputShell'
5
8
 
6
9
  const props = withDefaults(
7
10
  defineProps<{
@@ -10,12 +13,24 @@ const props = withDefaults(
10
13
  required?: boolean
11
14
  error?: string
12
15
  thin?: boolean
13
- outline?: boolean
14
16
  multiselect?: boolean
15
- }>(),
17
+ } & BagelInputShellProps>(),
16
18
  { multiselect: false },
17
19
  )
18
20
 
21
+ const { shellClass, shellStyle } = useBagelInputShell(() => ({
22
+ frame: props.frame,
23
+ outline: props.outline,
24
+ minWidth: props.minWidth,
25
+ maxWidth: props.maxWidth,
26
+ labelColor: props.labelColor,
27
+ labelActiveColor: props.labelActiveColor,
28
+ }))
29
+ const hasValue = computed(() => {
30
+ const v = selected.value
31
+ return Array.isArray(v) ? v.length > 0 : v != null && v !== ''
32
+ })
33
+
19
34
  const selected = defineModel<string | number | (string | number)[] | null>('modelValue', { default: null })
20
35
 
21
36
  function toggleOption(val: string | number) {
@@ -34,15 +49,28 @@ function isSelected(val: string | number) {
34
49
  </script>
35
50
 
36
51
  <template>
37
- <div class="bagel-input SelectBtn">
52
+ <div
53
+ class="bagel-input SelectBtn" :class="[shellClass, { 'has-value': hasValue }]" :style="shellStyle"
54
+ >
38
55
  <label v-if="label || required" class="pb-025">
39
56
  {{ resolveI18n(label) }}<span v-if="required"> *</span>
40
57
  </label>
41
58
  <div class="flex flex-wrap gap-05">
42
59
  <Btn
43
- v-for="option in options" :key="String(getOptionValue(option))" :thin="thin" :outline="outline"
44
- type="button" :class="isSelected(getOptionValue(option)!) ? 'primary color-white' : 'bg-transparent'"
45
- :style="{ color: 'var(--bgl-border-color)' }" class="border px-075 radius-2"
60
+ v-for="option in options" :key="String(getOptionValue(option))" :thin="thin"
61
+ type="button"
62
+ :class="isSelected(getOptionValue(option)!) ? 'color-white' : 'bg-transparent'"
63
+ :style="isSelected(getOptionValue(option)!)
64
+ ? {
65
+ backgroundColor: 'var(--bgl-input-label-active-color, var(--bgl-primary))',
66
+ borderColor: 'var(--bgl-input-label-active-color, var(--bgl-primary))',
67
+ color: 'white',
68
+ }
69
+ : {
70
+ color: 'var(--bgl-input-label-active-color, var(--bgl-border-color))',
71
+ borderColor: 'var(--bgl-input-label-active-color, var(--bgl-border-color))',
72
+ }"
73
+ class="border px-075 radius-2"
46
74
  @click="toggleOption(getOptionValue(option)!)"
47
75
  >
48
76
  <Icon v-if="getOptionIcon(option)" :icon="getOptionIcon(option)!" weight="300" />
@@ -5,6 +5,8 @@ import { Btn, Card, Skeleton, Dropdown, Icon, TextInput, useSearch, useI18n, res
5
5
  import { computed, onMounted, ref, watch } from 'vue'
6
6
  import { getOptionIcon, getOptionLabel, getOptionValue } from '../../../utils/options'
7
7
  import 'floating-vue/style.css'
8
+ import type { BagelInputShellProps } from './bagelInputShell'
9
+ import { useBagelInputShell } from './bagelInputShell'
8
10
 
9
11
  const props = withDefaults(defineProps<PropTypes>(), {
10
12
  placement: 'bottom-start',
@@ -14,12 +16,14 @@ const emit = defineEmits(['update:modelValue'])
14
16
 
15
17
  const { $t } = useI18n()
16
18
 
19
+ const { shellClass, shellStyle } = useBagelInputShell(props)
20
+
17
21
  type OptionsSource = Option[] | ((query: string) => Promise<Option[]>)
18
22
  const isAsyncSource = (src: OptionsSource): src is (q: string) => Promise<Option[]> => typeof src === 'function'
19
23
 
20
24
  type Primitive = string | number | boolean
21
25
 
22
- interface PropTypes {
26
+ interface PropTypes extends BagelInputShellProps {
23
27
  options: OptionsSource
24
28
  placeholder?: string
25
29
  disabled?: boolean
@@ -39,7 +43,6 @@ interface PropTypes {
39
43
  error?: string
40
44
  underlined?: boolean
41
45
  size?: 'xs' | 's' | 'm' | 'l' | 'xl'
42
- outline?: boolean
43
46
  border?: boolean
44
47
  thin?: boolean
45
48
  round?: boolean
@@ -50,10 +53,9 @@ const triggerBtn = ref<HTMLButtonElement | undefined>()
50
53
 
51
54
  const selectedItems = ref<Option[]>([])
52
55
 
53
- const getLabel = (option: Option) => getOptionLabel(option)
54
56
  const getValue = (option?: Option): Primitive | undefined => getOptionValue(option) as Primitive | undefined
55
57
 
56
- const selectedItemCount = computed(() => selectedItems.value.length ?? 0)
58
+ const selectedItemCount = computed(() => selectedItems.value.length)
57
59
  const searchTerm = ref<string>('')
58
60
 
59
61
  const dropdown = ref<InstanceType<typeof Dropdown> | undefined>()
@@ -77,11 +79,11 @@ const selectedLabel = computed((): string => {
77
79
  if (selectedItemCount.value > 4) {
78
80
  const str = selectedItems.value
79
81
  .slice(0, 4)
80
- .map(item => getLabel(item))
82
+ .map(item => getOptionLabel(item))
81
83
  .join(', ')
82
84
  return `${str}... +${selectedItemCount.value - 4}`
83
85
  }
84
- return selectedItems.value.map(item => getLabel(item)).join(', ')
86
+ return selectedItems.value.map(item => getOptionLabel(item)).join(', ')
85
87
  })
86
88
 
87
89
  const searchPlaceholder = computed(() => props.searchPlaceholder ?? selectedLabel.value ?? $t('select.search'))
@@ -172,7 +174,6 @@ function emitUpdate() {
172
174
  if (props.multiselect) {
173
175
  emit('update:modelValue', selectedItems.value.map(getValue).filter(v => v != null))
174
176
  } else {
175
- // selectedItems.value.splice(1, selectedItemCount.value - 1);
176
177
  const [item] = selectedItems.value
177
178
  emit('update:modelValue', getValue(item))
178
179
  }
@@ -230,9 +231,6 @@ watch(
230
231
  if (exists === undefined) { selectedItems.value.splice(i, 1) }
231
232
  else { selectedItems.value.splice(i, 1, exists) }
232
233
  })
233
- // const original = JSON.stringify(props.options.map(getValue));
234
- // const newSelection = JSON.stringify(selectedItems.value.map(getValue));
235
- // if (original !== newSelection) emitUpdate();
236
234
  },
237
235
  { deep: true, immediate: true },
238
236
  )
@@ -277,7 +275,8 @@ onMounted(() => {
277
275
  <template>
278
276
  <Dropdown
279
277
  ref="dropdown" v-model:shown="open" :placement="placement" class="bagel-input selectinput"
280
- :class="{ 'has-error': !!error, underlined, open, 'has-value': selectedItemCount > 0 }" no-auto-focus
278
+ :class="[shellClass, { 'has-error': !!error, underlined, open, 'has-value': selectedItemCount > 0 }]"
279
+ :style="shellStyle" no-auto-focus
281
280
  @click.stop
282
281
  >
283
282
  <template #trigger>
@@ -296,7 +295,7 @@ onMounted(() => {
296
295
  @keydown.down.prevent="navigate('down')" @keydown.up.prevent="navigate('up')"
297
296
  >
298
297
  <Icon v-if="icon" :icon="icon" />
299
- <p v-if="!hideLabel">
298
+ <p v-if="!hideLabel" :class="{ 'me-auto ms-05': icon }">
300
299
  {{ selectedItemCount > 0 ? selectedLabel : (underlined ? '' : (resolveI18n(placeholder)
301
300
  || 'Select...')) }}
302
301
  </p>
@@ -339,9 +338,9 @@ onMounted(() => {
339
338
  </template>
340
339
  <div class="flex gap-05">
341
340
  <Icon v-if="getOptionIcon(option)" :icon="getOptionIcon(option)!" :size="1" />
342
- <span class="block">
343
- {{ getLabel(option) }}
344
- </span>
341
+ <span class="block">
342
+ {{ getOptionLabel(option) }}
343
+ </span>
345
344
  </div>
346
345
  </div>
347
346
  <p v-if="results.length === 0" class="selectinput-option opacity-3 pointer-events-none">
@@ -377,7 +376,6 @@ onMounted(() => {
377
376
 
378
377
  .selectinput-options.multiselect .selectinput-option {
379
378
  grid-template-columns: 10px 1fr;
380
-
381
379
  }
382
380
 
383
381
  .selectinput-options {
@@ -410,7 +408,7 @@ onMounted(() => {
410
408
  }
411
409
 
412
410
  .selected {
413
- color: var(--bgl-primary);
411
+ color: var(--bgl-input-label-active-color, var(--bgl-primary));
414
412
  background: var(--bgl-selected);
415
413
  }
416
414
  </style>
@@ -448,10 +446,6 @@ onMounted(() => {
448
446
  .selectinput-btn.selectinput-btn--round { border-radius: 999px; }
449
447
  .selectinput-btn.selectinput-btn--thin { height: calc(var(--input-height) * 0.75); padding: 0 0.5rem; }
450
448
 
451
- .selectinput .bagel-input.mb-0 input {
452
- /* background: transparent !important; */
453
- }
454
-
455
449
  .selectinput-btn:disabled {
456
450
  color: var(--input-disabled-color);
457
451
  background-color: transparent;
@@ -464,11 +458,11 @@ onMounted(() => {
464
458
 
465
459
  .selectinput.has-error .selectinput-btn {
466
460
  border-color: var(--bgl-red, #dc3545) !important;
467
- outline: 1px solid var(--bgl-red, #dc3545) !important;
468
- }
461
+ outline: 1px solid var(--bgl-red, #dc3545) !important;
462
+ }
469
463
 
470
- .selectinput.underlined.has-error .selectinput-btn {
471
- border-color: var(--bgl-red, #dc3545) !important;
464
+ .selectinput.underlined.has-error .selectinput-btn {
465
+ border-color: var(--bgl-red, #dc3545) !important;
472
466
  }
473
467
 
474
468
  /* Underlined mode styling */
@@ -4,6 +4,8 @@ import { Btn } from '@bagelink/vue'
4
4
  import { useEventListener, useResizeObserver } from '@vueuse/core'
5
5
  import SignaturePad from 'signature_pad'
6
6
  import { computed, onMounted, ref, watch } from 'vue'
7
+ import type { BagelInputShellProps } from './bagelInputShell'
8
+ import { useBagelInputShell } from './bagelInputShell'
7
9
 
8
10
  export interface WaterMark {
9
11
  text: string
@@ -33,10 +35,14 @@ const props = withDefaults(defineProps<{
33
35
  required?: boolean
34
36
  label?: string
35
37
  helptext?: string
36
- }>(), {
38
+ penColor?: string
39
+ backgroundColor?: string
40
+ } & BagelInputShellProps>(), {
37
41
  clearable: true,
38
42
  })
39
43
 
44
+ const { shellClass, shellStyle } = useBagelInputShell(props)
45
+
40
46
  const sigData = defineModel()
41
47
  const fileData = defineModel<File>('file')
42
48
 
@@ -108,10 +114,15 @@ function undo() {
108
114
 
109
115
  const defaultOptions = ref<SignaturePadOptions>({
110
116
  penColor: 'rgb(0, 0, 0)',
111
- backgroundColor: 'rgb(255, 255, 255)',
117
+ backgroundColor: 'rgba(0,0,0,0)',
112
118
  })
113
119
 
114
- const signatureOptions = computed(() => ({ ...defaultOptions.value, ...props.sigOption }))
120
+ const signatureOptions = computed(() => ({
121
+ ...defaultOptions.value,
122
+ ...(props.penColor ? { penColor: props.penColor } : {}),
123
+ ...(props.backgroundColor ? { backgroundColor: props.backgroundColor } : {}),
124
+ ...props.sigOption,
125
+ }))
115
126
 
116
127
  watch(
117
128
  () => props.disabled,
@@ -121,6 +132,14 @@ watch(
121
132
  },
122
133
  )
123
134
 
135
+ watch(signatureOptions, (opts) => {
136
+ if (!sig.value) { return }
137
+ sig.value.penColor = opts.penColor ?? 'rgb(0,0,0)'
138
+ if (opts.backgroundColor) {
139
+ sig.value.backgroundColor = opts.backgroundColor
140
+ }
141
+ })
142
+
124
143
  function addWaterMark(data: WaterMark) {
125
144
  if (!(Object.prototype.toString.call(data) === '[object Object]')) {
126
145
  throw new Error(`Expected Object, got ${typeof data}.`)
@@ -216,11 +235,11 @@ defineExpose({
216
235
 
217
236
  <template>
218
237
  <div
219
- class="bgl_input signature-pad relative"
220
- :class="{ 'bg-transparent': disabled }"
221
- @touchmove.prevent
238
+ class="bagel-input bgl_input signature-pad relative"
239
+ :class="[shellClass, { 'has-value': !_isEmpty }]" :style="shellStyle"
240
+ tabindex="-1" @touchmove.prevent
222
241
  >
223
- <label v-if="label" class="label">{{ label }}</label>
242
+ <label v-if="label" class="label-text">{{ label }}</label>
224
243
  <p v-if="helptext" class="helptext">{{ helptext }}</p>
225
244
  <Btn
226
245
  v-if="clearable && !disabled"
@@ -234,6 +253,7 @@ defineExpose({
234
253
  ref="vCanvas"
235
254
  class="canvas"
236
255
  :disabled
256
+ :style="backgroundColor ? { background: backgroundColor } : {}"
237
257
  />
238
258
  <input
239
259
  placeholder="required"
@@ -257,22 +277,27 @@ defineExpose({
257
277
  }
258
278
 
259
279
  .bgl_input.signature-pad {
260
- background: var(--input-bg);
280
+ background: transparent;
261
281
  border: none;
262
282
  border-radius: var(--input-border-radius);
263
283
  }
264
284
 
265
- .bgl_input.signature-pad > .label {
285
+ .bgl_input.signature-pad .canvas {
286
+ background: var(--input-bg);
287
+ border-radius: var(--input-border-radius);
288
+ }
289
+
290
+ .bgl_input.signature-pad > .label-text {
266
291
  display: block;
267
- font-size: 0.8rem;
268
- padding: 0.25rem 0.5rem 0;
292
+ font-size: var(--label-font-size);
293
+ margin-bottom: 0.25rem;
269
294
  }
270
295
 
271
296
  .bgl_input.signature-pad > .helptext {
272
- font-size: 0.75rem;
273
- padding: 0.15rem 0.5rem 0.25rem;
274
- margin: 0;
297
+ font-size: var(--label-font-size);
298
+ margin: 0 0 0.25rem 0;
275
299
  line-height: 1.4;
300
+ opacity: 0.7;
276
301
  }
277
302
 
278
303
  .bgl_input.signature-pad .canvas[disabled] {
@@ -2,6 +2,8 @@
2
2
  import { Btn, Icon, formatString } from '@bagelink/vue'
3
3
  import { onMounted, ref } from 'vue'
4
4
  import { VueDraggableNext } from 'vue-draggable-next'
5
+ import type { BagelInputShellProps } from './bagelInputShell'
6
+ import { useBagelInputShell } from './bagelInputShell'
5
7
 
6
8
  const props = withDefaults(
7
9
  defineProps<{
@@ -10,7 +12,7 @@ const props = withDefaults(
10
12
  fieldname: string
11
13
  bagelApp?: any
12
14
  modelValue: any
13
- }>(),
15
+ } & BagelInputShellProps>(),
14
16
  {
15
17
  description: '',
16
18
  bagelApp: undefined,
@@ -63,6 +65,8 @@ function ended() {
63
65
  })
64
66
  emits('update:modelValue', listRef.value)
65
67
  }
68
+ const { shellClass, shellStyle } = useBagelInputShell(props)
69
+
66
70
  onMounted(() => {
67
71
  bagel = props.bagelApp || api
68
72
  })
@@ -73,7 +77,7 @@ onMounted(() => {
73
77
  class="table-field-wrap"
74
78
  :title="description"
75
79
  >
76
- <div class="bagel-input">
80
+ <div class="bagel-input" :class="shellClass" :style="shellStyle">
77
81
  <label>
78
82
  {{ fieldMeta?.label }}
79
83
  </label>
@@ -4,6 +4,8 @@ import type { CountryCode } from 'libphonenumber-js'
4
4
  import { Dropdown, Flag, Icon, TextInput, allCountries, ipapi, useI18n, resolveI18n } from '@bagelink/vue'
5
5
  import { parsePhoneNumberFromString } from 'libphonenumber-js'
6
6
  import { computed, onMounted, ref, watch } from 'vue'
7
+ import type { BagelInputShellProps } from './bagelInputShell'
8
+ import { useBagelInputShell } from './bagelInputShell'
7
9
 
8
10
  const props = defineProps<{
9
11
  id?: string
@@ -16,7 +18,9 @@ const props = defineProps<{
16
18
  disabled?: boolean
17
19
  underlined?: boolean
18
20
  error?: string
19
- }>()
21
+ } & BagelInputShellProps>()
22
+
23
+ const { shellClass, shellStyle } = useBagelInputShell(props)
20
24
 
21
25
  const emit = defineEmits(['update:modelValue', 'blur', 'focus', 'keydown', 'input', 'paste'])
22
26
 
@@ -230,7 +234,10 @@ onMounted(initializeCountry)
230
234
  </script>
231
235
 
232
236
  <template>
233
- <div class="bagel-input text-input" :class="{ 'invalid': !isValid, 'has-error': !!error, underlined, 'has-value': hasValue }">
237
+ <div
238
+ class="bagel-input text-input" :class="[shellClass, { 'invalid': !isValid, 'has-error': !!error, underlined, 'has-value': hasValue }]"
239
+ :style="shellStyle"
240
+ >
234
241
  <label>
235
242
  <span v-if="label || (underlined && placeholder)" class="label-text">{{ resolveI18n(label) || resolveI18n(placeholder) }}<span v-if="required"> *</span></span>
236
243
  <div
@@ -302,14 +309,49 @@ onMounted(initializeCountry)
302
309
  box-shadow: inset 0 0 10px #00000012;
303
310
  }
304
311
 
312
+ /* In frame mode, remove the solid background from .tel-input wrapper */
313
+ .bagel-input.frame .tel-input {
314
+ background: transparent !important;
315
+ }
316
+
317
+ /* frame variant: outline on the tel-input wrapper */
318
+ .bagel-input.frame .tel-input {
319
+ outline: 1.5px solid var(--border-color);
320
+ outline-offset: -1px;
321
+ transition: outline-color 0.2s ease, box-shadow 0.2s ease;
322
+ }
323
+
324
+ .bagel-input.frame .tel-input:focus-within {
325
+ outline-color: var(--bgl-input-label-active-color, var(--bgl-primary)) !important;
326
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--bgl-input-label-active-color, var(--bgl-primary)) 12%, transparent) !important;
327
+ }
328
+
329
+ /* bgl-outline variant: outline on the tel-input wrapper */
330
+ .bagel-input.bgl-outline .tel-input {
331
+ outline: 1.5px solid var(--border-color);
332
+ outline-offset: -1px;
333
+ }
334
+
335
+ /* Prevent the global frame/outline rules from hitting the inner inputs */
336
+ .bagel-input.frame .tel-input input,
337
+ .bagel-input.bgl-outline .tel-input input,
338
+ .bagel-input.frame .tel-input input:focus,
339
+ .bagel-input.frame .tel-input input:focus-visible {
340
+ outline: none !important;
341
+ box-shadow: none !important;
342
+ background: transparent;
343
+ }
344
+
305
345
  .tel-input input {
306
346
  background: transparent;
307
347
  text-align: left;
308
348
  flex: 1;
309
349
  }
310
350
 
351
+ .tel-input input:focus,
311
352
  .tel-input input:focus-visible {
312
- box-shadow: none;
353
+ outline: none !important;
354
+ box-shadow: none !important;
313
355
  }
314
356
 
315
357
  .country-code-display {
@@ -342,6 +384,12 @@ onMounted(initializeCountry)
342
384
  margin-left: 4px;
343
385
  }
344
386
 
387
+ .national-number-input:focus,
388
+ .national-number-input:focus-visible {
389
+ outline: none !important;
390
+ box-shadow: none !important;
391
+ }
392
+
345
393
  .country-changed {
346
394
  animation: highlight-country 1.5s ease-in-out;
347
395
  }
@@ -391,7 +439,7 @@ onMounted(initializeCountry)
391
439
 
392
440
  /* When focused in underlined mode, make label primary color */
393
441
  .bagel-input.underlined:focus-within .label-text {
394
- color: var(--bgl-primary);
442
+ color: var(--bgl-input-label-active-color, var(--bgl-primary));
395
443
  opacity: 1;
396
444
  }
397
445