@bagelink/vue 1.15.202 → 1.15.205

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 (69) hide show
  1. package/dist/components/AddressSearch.vue.d.ts.map +1 -1
  2. package/dist/components/Swiper.vue.d.ts +2 -2
  3. package/dist/components/calendar/CalendarPopover.vue.d.ts.map +1 -1
  4. package/dist/components/charts/BarChart.vue.d.ts.map +1 -1
  5. package/dist/components/charts/ChartTooltip.vue.d.ts.map +1 -1
  6. package/dist/components/charts/Donut.vue.d.ts.map +1 -1
  7. package/dist/components/charts/Funnel.vue.d.ts.map +1 -1
  8. package/dist/components/charts/Gauge.vue.d.ts.map +1 -1
  9. package/dist/components/charts/LineChart.vue.d.ts.map +1 -1
  10. package/dist/components/charts/MapChart.vue.d.ts +25 -0
  11. package/dist/components/charts/MapChart.vue.d.ts.map +1 -0
  12. package/dist/components/charts/RadialBars.vue.d.ts.map +1 -1
  13. package/dist/components/charts/RankBars.vue.d.ts.map +1 -1
  14. package/dist/components/charts/StatCard.vue.d.ts.map +1 -1
  15. package/dist/components/charts/core/arc.d.ts +6 -0
  16. package/dist/components/charts/core/arc.d.ts.map +1 -0
  17. package/dist/components/charts/core/useChartTip.d.ts +13 -0
  18. package/dist/components/charts/core/useChartTip.d.ts.map +1 -0
  19. package/dist/components/charts/core/world-paths.d.ts +13 -0
  20. package/dist/components/charts/core/world-paths.d.ts.map +1 -0
  21. package/dist/components/charts/index.d.ts +2 -0
  22. package/dist/components/charts/index.d.ts.map +1 -1
  23. package/dist/components/charts/mapTypes.d.ts +7 -0
  24. package/dist/components/charts/mapTypes.d.ts.map +1 -0
  25. package/dist/components/dataTable/DataTable.vue.d.ts.map +1 -1
  26. package/dist/components/form/inputs/DateInput.vue.d.ts +3 -0
  27. package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
  28. package/dist/components/form/inputs/NumberInput.vue.d.ts.map +1 -1
  29. package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
  30. package/dist/components/form/inputs/index.d.ts +0 -1
  31. package/dist/components/form/inputs/index.d.ts.map +1 -1
  32. package/dist/components/index.d.ts +2 -2
  33. package/dist/components/index.d.ts.map +1 -1
  34. package/dist/form-flow/FormFlowPlayer.vue.d.ts +18 -0
  35. package/dist/form-flow/FormFlowPlayer.vue.d.ts.map +1 -1
  36. package/dist/form-flow/structure.d.ts +12 -0
  37. package/dist/form-flow/structure.d.ts.map +1 -1
  38. package/dist/index.cjs +177 -154
  39. package/dist/index.mjs +37661 -43116
  40. package/dist/style.css +1 -2
  41. package/package.json +1 -1
  42. package/src/components/charts/BarChart.vue +15 -15
  43. package/src/components/charts/ChartTooltip.vue +7 -6
  44. package/src/components/charts/Donut.vue +25 -40
  45. package/src/components/charts/Funnel.vue +10 -12
  46. package/src/components/charts/Gauge.vue +4 -2
  47. package/src/components/charts/LineChart.vue +15 -17
  48. package/src/components/charts/MapChart.vue +198 -0
  49. package/src/components/charts/RadialBars.vue +3 -1
  50. package/src/components/charts/RankBars.vue +2 -0
  51. package/src/components/charts/Sparkline.vue +1 -1
  52. package/src/components/charts/StatCard.vue +2 -0
  53. package/src/components/charts/core/arc.ts +53 -0
  54. package/src/components/charts/core/useChartTip.ts +35 -0
  55. package/src/components/charts/core/world-paths.ts +351 -0
  56. package/src/components/charts/index.ts +3 -0
  57. package/src/components/charts/mapTypes.ts +6 -0
  58. package/src/components/dataTable/useTableVirtualization.ts +5 -5
  59. package/src/components/form/inputs/DateInput.vue +9 -1
  60. package/src/components/form/inputs/NumberInput.vue +17 -6
  61. package/src/components/form/inputs/index.ts +0 -1
  62. package/src/components/index.ts +2 -2
  63. package/src/form-flow/FormFlowPlayer.vue +49 -0
  64. package/src/form-flow/structure.ts +12 -0
  65. package/dist/components/dataTable/useTableVirtualization.d.ts +0 -25
  66. package/dist/components/dataTable/useTableVirtualization.d.ts.map +0 -1
  67. package/dist/components/form/inputs/JSONInput.vue.d.ts +0 -21
  68. package/dist/components/form/inputs/JSONInput.vue.d.ts.map +0 -1
  69. package/src/components/form/inputs/JSONInput.vue +0 -60
@@ -4,9 +4,12 @@ export { default as BarChart } from './BarChart.vue'
4
4
  export { default as Donut } from './Donut.vue'
5
5
  export { default as Funnel } from './Funnel.vue'
6
6
  export { default as Gauge } from './Gauge.vue'
7
+ export { default as MapChart } from './MapChart.vue'
7
8
  export { default as RadialBars } from './RadialBars.vue'
8
9
  export { default as RankBars } from './RankBars.vue'
9
10
  export { default as StatCard } from './StatCard.vue'
10
11
 
12
+ export type { MapDatum } from './mapTypes'
13
+
11
14
  export type { RawPoint, RawSeries, NormalizeOptions } from './core/data'
12
15
  export type { ValueFormat } from './core/format'
@@ -0,0 +1,6 @@
1
+ export interface MapDatum {
2
+ /** ISO 3166-1 alpha-2 country code (e.g. "US", "IL"). */
3
+ id: string
4
+ label?: string
5
+ value: number
6
+ }
@@ -11,9 +11,10 @@ export function useTableVirtualization<T>(options: TableVirtualizationOptions<T>
11
11
  return [{ __isHeader: true } as any, ...originalData]
12
12
  })
13
13
 
14
- // For useVirtualList, which expects MaybeRef
14
+ // `as any`: packages/vue nests vue@3.5.40 while @vueuse/core is typed against
15
+ // workspace vue@3.5.35 — RefSymbol identities diverge under root tsc.
15
16
  const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
16
- dataWithHeader,
17
+ dataWithHeader as any,
17
18
  {
18
19
  itemHeight: options.itemHeight,
19
20
  overscan: 10,
@@ -28,11 +29,10 @@ export function useTableVirtualization<T>(options: TableVirtualizationOptions<T>
28
29
  async function registerLastItemObserver() {
29
30
  await until(() => lastItemEl.value).toBeTruthy()
30
31
 
31
- useIntersectionObserver(lastItemEl, ([entry]) => {
32
- // Check if options.data.value exists and has length
32
+ useIntersectionObserver(lastItemEl as any, ([entry]) => {
33
33
  const dataLength = toValue(options.data).length || 0
34
34
 
35
- if (entry.isIntersecting && dataLength > 0) {
35
+ if (entry?.isIntersecting && dataLength > 0) {
36
36
  options.onLastItemVisible?.()
37
37
  }
38
38
  })
@@ -36,6 +36,8 @@ export interface DateInputProps extends BagelInputShellProps {
36
36
  center?: boolean
37
37
  error?: string
38
38
  underlined?: boolean
39
+ /** When true, input is not editable (same as `edit-mode="false"`). */
40
+ disabled?: boolean
39
41
  }
40
42
 
41
43
  const props = withDefaults(
@@ -43,6 +45,7 @@ const props = withDefaults(
43
45
  {
44
46
  enableTime: false,
45
47
  editMode: true,
48
+ disabled: false,
46
49
  small: false,
47
50
  range: false,
48
51
  mode: () => ({ mode: 'day' }),
@@ -51,6 +54,8 @@ const props = withDefaults(
51
54
  },
52
55
  )
53
56
 
57
+ const isEditable = computed(() => props.editMode && !props.disabled)
58
+
54
59
  const { $t } = useI18n()
55
60
 
56
61
  const { shellClass, shellStyle } = useBagelInputShell(props)
@@ -112,6 +117,9 @@ function normalizeDate(date: Date): string {
112
117
  function coerceDateOnlyValue(val: string | Date | DateRangeValue | undefined): string | Date | DateRangeValue | undefined {
113
118
  if (props.enableTime || props.range || val == null) return val
114
119
  if (typeof val === 'string' && val.includes('T')) {
120
+ // Prefer the calendar day from the string — avoids TZ shifting midnight UTC/local.
121
+ const day = /^(\d{4}-\d{2}-\d{2})/.exec(val)?.[1]
122
+ if (day) return day
115
123
  const d = new Date(val)
116
124
  if (!Number.isNaN(d.getTime())) return local.date(d)
117
125
  }
@@ -312,7 +320,7 @@ onMounted(() => {
312
320
  <TextInput
313
321
  v-model="inputValue" iconStart="calendar"
314
322
  :placeholder="underlined ? ' ' : (resolveI18n(placeholder) || defaultPlaceholder)"
315
- :required="underlined ? false : required" :disabled="!editMode"
323
+ :required="underlined ? false : required" :disabled="!isEditable"
316
324
  :error="!isValid ? $t('date.invalidFormat') : error" :class="{ 'txt-center': center }"
317
325
  :underlined="underlined" :frame="frame" :outline="outline" :min-width="minWidth"
318
326
  :max-width="maxWidth" :label-color="labelColor" :label-active-color="labelActiveColor"
@@ -146,12 +146,23 @@ watch(() => numberValue.value, () => {
146
146
  })
147
147
 
148
148
  watch(() => modelValue, (newVal) => {
149
- if (newVal !== numberValue.value) {
150
- if (typeof newVal === 'string' && (newVal === '-' || newVal.endsWith('.'))) {
151
- formattedValue.value = newVal
152
- return
153
- }
154
- numberValue.value = Number.parseFloat(`${newVal}`) || 0
149
+ if (newVal === undefined || newVal === null || newVal === '') {
150
+ numberValue.value = undefined
151
+ formattedValue.value = ''
152
+ return
153
+ }
154
+ if (typeof newVal === 'string' && (newVal === '-' || newVal.endsWith('.'))) {
155
+ formattedValue.value = newVal
156
+ return
157
+ }
158
+ const parsed = Number.parseFloat(`${newVal}`)
159
+ if (Number.isNaN(parsed)) {
160
+ numberValue.value = undefined
161
+ formattedValue.value = ''
162
+ return
163
+ }
164
+ if (parsed !== numberValue.value) {
165
+ numberValue.value = parsed
155
166
  }
156
167
  }, { immediate: true })
157
168
 
@@ -7,7 +7,6 @@ export { default as ColorInput } from './ColorInput.vue'
7
7
  export { default as DateInput } from './DateInput.vue'
8
8
  export { default as DatePicker } from './DatePicker.vue'
9
9
  export { default as EmailInput } from './EmailInput.vue'
10
- export { default as JSONInput } from './JSONInput.vue'
11
10
  export { default as MarkdownEditor } from './MarkdownEditor.vue'
12
11
  export { default as NumberInput } from './NumberInput.vue'
13
12
  export { default as OTP } from './OTP.vue'
@@ -3,8 +3,8 @@ export { default as AccordionItem } from './AccordionItem.vue'
3
3
  export { default as AddressSearch } from './AddressSearch.vue'
4
4
  export { default as AddToCalendar } from './AddToCalendar.vue'
5
5
  export { default as Alert } from './Alert.vue'
6
- export { BarChart, Donut, Funnel, Gauge, LineChart, RadialBars, RankBars, Sparkline, StatCard } from './charts'
7
- export type { NormalizeOptions, RawPoint, RawSeries, ValueFormat } from './charts'
6
+ export { BarChart, Donut, Funnel, Gauge, LineChart, MapChart, RadialBars, RankBars, Sparkline, StatCard } from './charts'
7
+ export type { MapDatum, NormalizeOptions, RawPoint, RawSeries, ValueFormat } from './charts'
8
8
  export { default as Avatar } from './Avatar.vue'
9
9
  export { default as Badge } from './Badge.vue'
10
10
  /** @deprecated Renamed to Badge. Pill is an alias that will be removed in a future version. */
@@ -37,6 +37,18 @@ export interface Props {
37
37
  autoAdvance?: boolean
38
38
  /** Delay (ms) before an auto-advance fires, letting the choice paint. */
39
39
  autoAdvanceDelay?: number
40
+ /**
41
+ * Push each step's `enterEvent` / `submitEvent` payload to `window.dataLayer`
42
+ * (GTM). Default true. Set false to suppress (e.g. SSR or when the host wants
43
+ * to handle analytics itself via the `dataLayer` event).
44
+ */
45
+ dataLayer?: boolean
46
+ /**
47
+ * Perform `redirectUrl` navigation on submit. Default true. Set false to let
48
+ * the host own navigation (it can read the URL from the `complete` event or
49
+ * the `redirect` event).
50
+ */
51
+ redirect?: boolean
40
52
  /** Optional async submit handler; awaited before `complete` is emitted. */
41
53
  onComplete?: (data: Record<string, any>) => Promise<void> | void
42
54
  }
@@ -45,6 +57,8 @@ const props = withDefaults(defineProps<Props>(), {
45
57
  animated: true,
46
58
  autoAdvance: true,
47
59
  autoAdvanceDelay: 200,
60
+ dataLayer: true,
61
+ redirect: true,
48
62
  })
49
63
 
50
64
  const emit = defineEmits<{
@@ -52,6 +66,10 @@ const emit = defineEmits<{
52
66
  'stepChange': [stepKey: string]
53
67
  /** Best-effort 0–100 progress along the current path; recomputes on change. */
54
68
  'progress': [value: number]
69
+ /** A dataLayer payload was pushed. `phase` = which step hook fired it. */
70
+ 'dataLayer': [payload: Record<string, any>, phase: 'enter' | 'submit']
71
+ /** The form is about to redirect to `url` (fired even if `redirect` is off). */
72
+ 'redirect': [url: string]
55
73
  'complete': [data: Record<string, any>]
56
74
  }>()
57
75
 
@@ -90,6 +108,26 @@ watch(currentKey, (key) => {
90
108
 
91
109
  const currentStep = computed(() => stepByKey(props.structure, currentKey.value ?? null))
92
110
 
111
+ // ─── dataLayer (GTM) ─────────────────────────────────────────────────────────
112
+ // Push a step's analytics payload to window.dataLayer and notify the host. The
113
+ // server stores these payloads verbatim; the client decides when to fire them:
114
+ // `enterEvent` on step entry, `submitEvent` on submit-from-a-terminal-step.
115
+ function pushDataLayer(payload: Record<string, any> | null | undefined, phase: 'enter' | 'submit') {
116
+ if (!payload || typeof payload !== 'object') return
117
+ emit('dataLayer', payload, phase)
118
+ if (!props.dataLayer || typeof window === 'undefined') return
119
+ const w = window as unknown as { dataLayer?: Record<string, any>[] }
120
+ w.dataLayer = w.dataLayer || []
121
+ w.dataLayer.push({ ...payload })
122
+ }
123
+
124
+ // Fire the current step's `enterEvent` whenever we land on it (including the
125
+ // first step). `immediate` covers the entry step; the key guard avoids
126
+ // re-firing when the same step object recomputes.
127
+ watch(currentKey, (key, prev) => {
128
+ if (key && key !== prev) pushDataLayer(stepByKey(props.structure, key)?.enterEvent, 'enter')
129
+ }, { immediate: true })
130
+
93
131
  // Convert the current step's stored JSON Schema into a renderable schema.
94
132
  const currentSchema = computed(() => {
95
133
  const step = currentStep.value
@@ -124,9 +162,20 @@ async function onPrimary() {
124
162
 
125
163
  if (isTerminal.value) {
126
164
  submitting.value = true
165
+ const step = currentStep.value
127
166
  try {
128
167
  await props.onComplete?.({ ...formData.value })
168
+ // Fire this terminal step's submit analytics before completing.
169
+ pushDataLayer(step?.submitEvent, 'submit')
129
170
  emit('complete', { ...formData.value })
171
+ // Redirect last: leaves the page after analytics + host handling.
172
+ const url = step?.redirectUrl?.trim()
173
+ if (url) {
174
+ emit('redirect', url)
175
+ if (props.redirect && typeof window !== 'undefined') {
176
+ window.location.assign(url)
177
+ }
178
+ }
130
179
  }
131
180
  finally {
132
181
  submitting.value = false
@@ -26,6 +26,18 @@ export interface FormStep {
26
26
  * default: a single required radio/select field. `false` force-disables.
27
27
  */
28
28
  autoAdvance?: boolean
29
+ /**
30
+ * Where to send the browser after the form is submitted *from this step*
31
+ * (final step or an early-termination leaf). Empty => no redirect.
32
+ */
33
+ redirectUrl?: string | null
34
+ /** dataLayer payload pushed when the user *enters* this step. */
35
+ enterEvent?: Record<string, any> | null
36
+ /**
37
+ * dataLayer payload pushed when the form is *submitted* from this step
38
+ * (i.e. this step is terminal), before the redirect.
39
+ */
40
+ submitEvent?: Record<string, any> | null
29
41
  /** Canvas position (designer-only; ignored by the server). */
30
42
  x?: number
31
43
  y?: number
@@ -1,25 +0,0 @@
1
- import { TableVirtualizationOptions } from '../../types/TableSchema';
2
- export declare function useTableVirtualization<T>(options: TableVirtualizationOptions<T>): {
3
- list: import('vue').ComputedRef<import('@vueuse/core').UseVirtualListItem<any>[]>;
4
- containerProps: {
5
- ref: import('vue').Ref<HTMLElement | null>;
6
- onScroll: () => void;
7
- style: import('vue').StyleValue;
8
- };
9
- wrapperProps: import('vue').ComputedRef<{
10
- style: {
11
- width: string;
12
- height: string;
13
- marginTop: string;
14
- } | {
15
- width: string;
16
- height: string;
17
- marginLeft: string;
18
- display: string;
19
- };
20
- }>;
21
- scrollTo: (index: number) => void;
22
- lastItemEl: import('vue').Ref<HTMLTableRowElement | null, HTMLTableRowElement | null>;
23
- registerLastItemObserver: () => Promise<void>;
24
- };
25
- //# sourceMappingURL=useTableVirtualization.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useTableVirtualization.d.ts","sourceRoot":"","sources":["../../../src/components/dataTable/useTableVirtualization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAA;AAIzE,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,OAAO,EAAE,0BAA0B,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;EA4C/E"}
@@ -1,21 +0,0 @@
1
- import { BagelInputShellProps } from './bagelInputShell';
2
- type __VLS_Props = {
3
- description?: string;
4
- label?: string;
5
- modelValue: any;
6
- placeholder?: string;
7
- editMode?: boolean;
8
- small?: boolean;
9
- } & BagelInputShellProps;
10
- declare const _default: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
11
- "update:modelValue": (...args: any[]) => void;
12
- }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
13
- "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
14
- }>, {
15
- label: string;
16
- description: string;
17
- placeholder: string;
18
- editMode: boolean;
19
- }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, HTMLDivElement>;
20
- export default _default;
21
- //# sourceMappingURL=JSONInput.vue.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"JSONInput.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/form/inputs/JSONInput.vue"],"names":[],"mappings":"AAiEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAG7D,KAAK,WAAW,GAAG;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,GAAG,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;CACf,GAAG,oBAAoB,CAAC;;;;;;WALhB,MAAM;iBADA,MAAM;iBAGN,MAAM;cACT,OAAO;;AAkGpB,wBASG"}
@@ -1,60 +0,0 @@
1
- <script setup lang="ts">
2
- // const props =
3
- import { computed } from 'vue'
4
- import { resolveI18n } from '@bagelink/vue'
5
- import type { BagelInputShellProps } from './bagelInputShell'
6
- import { useBagelInputShell } from './bagelInputShell'
7
-
8
- const props = withDefaults(
9
- defineProps<{
10
- description?: string
11
- label?: string
12
- modelValue: any
13
- placeholder?: string
14
- editMode?: boolean
15
- small?: boolean
16
- } & BagelInputShellProps>(),
17
- {
18
- description: '',
19
- editMode: true,
20
- placeholder: '',
21
- label: '',
22
- },
23
- )
24
-
25
- const { shellClass, shellStyle } = useBagelInputShell(props)
26
- const hasValue = computed(() => props.modelValue != null && String(props.modelValue).length > 0)
27
-
28
- const emits = defineEmits(['update:modelValue'])
29
- function handleInput(e: Event) {
30
- const el = e.target as HTMLInputElement
31
- emits('update:modelValue', el.value)
32
- }
33
- </script>
34
-
35
- <template>
36
- <div
37
- class="bagel-input" :title="description"
38
- :class="[shellClass, { small, 'has-value': hasValue }]" :style="shellStyle"
39
- >
40
- <label v-if="label">
41
- <LangText :input="label" />
42
- </label>
43
- <textarea
44
- :value="modelValue" :class="{ 'no-edit': !editMode }" :placeholder="resolveI18n(placeholder)"
45
- @input="handleInput"
46
- />
47
- </div>
48
- </template>
49
-
50
- <style scoped>
51
-
52
- .bagel-input textarea {
53
- font-family: monospace;
54
- }
55
-
56
- [dir='rtl'] .bagel-input textarea {
57
- direction: ltr;
58
- }
59
-
60
- </style>