@bagelink/vue 1.14.15 → 1.15.0

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 (102) hide show
  1. package/dist/components/Alert.vue.d.ts.map +1 -1
  2. package/dist/components/Badge.vue.d.ts.map +1 -1
  3. package/dist/components/Btn.vue.d.ts.map +1 -1
  4. package/dist/components/Dropdown.vue.d.ts.map +1 -1
  5. package/dist/components/Image.vue.d.ts.map +1 -1
  6. package/dist/components/ListItem.vue.d.ts.map +1 -1
  7. package/dist/components/MapEmbed/Index.vue.d.ts.map +1 -1
  8. package/dist/components/Pagination.vue.d.ts.map +1 -1
  9. package/dist/components/Swiper.vue.d.ts.map +1 -1
  10. package/dist/components/Toast.vue.d.ts.map +1 -1
  11. package/dist/components/form/index.d.ts.map +1 -1
  12. package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
  13. package/dist/components/index.d.ts.map +1 -1
  14. package/dist/components/layout/AppContent.vue.d.ts.map +1 -1
  15. package/dist/components/layout/AppLayout.vue.d.ts.map +1 -1
  16. package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -1
  17. package/dist/components/layout/Panel.vue.d.ts.map +1 -1
  18. package/dist/components/layout/Resizable.vue.d.ts.map +1 -1
  19. package/dist/components/layout/TabsNav.vue.d.ts.map +1 -1
  20. package/dist/components/layout/appLayoutContext.d.ts +24 -0
  21. package/dist/components/layout/appLayoutContext.d.ts.map +1 -0
  22. package/dist/components/layout/index.d.ts.map +1 -1
  23. package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
  24. package/dist/composables/index.d.ts.map +1 -1
  25. package/dist/composables/useDevice.d.ts.map +1 -1
  26. package/dist/composables/useEscapeKey.d.ts +12 -0
  27. package/dist/composables/useEscapeKey.d.ts.map +1 -0
  28. package/dist/composables/useSchemaField.d.ts.map +1 -1
  29. package/dist/composables/useTheme.d.ts.map +1 -1
  30. package/dist/form-flow/FormFlow.vue.d.ts.map +1 -1
  31. package/dist/form-flow/form-flow.d.ts.map +1 -1
  32. package/dist/index.cjs +203 -207
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.mjs +25819 -28870
  35. package/dist/style.css +1 -1
  36. package/dist/types/BagelForm.d.ts.map +1 -1
  37. package/dist/types/BtnOptions.d.ts.map +1 -1
  38. package/dist/utils/constants.d.ts.map +1 -1
  39. package/dist/utils/index.d.ts.map +1 -1
  40. package/package.json +3 -6
  41. package/src/components/Alert.vue +34 -14
  42. package/src/components/Badge.vue +145 -22
  43. package/src/components/Btn.vue +43 -31
  44. package/src/components/Dropdown.vue +5 -12
  45. package/src/components/FilterQuery.vue +1 -1
  46. package/src/components/Image.vue +3 -2
  47. package/src/components/JSONSchema.vue +2 -2
  48. package/src/components/JsonBuilder.vue +1 -1
  49. package/src/components/ListItem.vue +1 -3
  50. package/src/components/MapEmbed/Index.vue +10 -9
  51. package/src/components/NavBar.vue +2 -2
  52. package/src/components/Spreadsheet/Index.vue +1 -1
  53. package/src/components/Swiper.vue +3 -1
  54. package/src/components/Toast.vue +23 -8
  55. package/src/components/calendar/Index.vue +4 -4
  56. package/src/components/calendar/views/MonthView.vue +3 -3
  57. package/src/components/form/index.ts +0 -4
  58. package/src/components/form/inputs/EmailInput.vue +1 -1
  59. package/src/components/form/inputs/NumberInput.vue +1 -1
  60. package/src/components/form/inputs/OTP.vue +2 -2
  61. package/src/components/form/inputs/SelectInput.vue +3 -3
  62. package/src/components/form/inputs/TelInput.vue +2 -2
  63. package/src/components/form/inputs/TextInput.vue +1 -1
  64. package/src/components/form/inputs/Upload/upload.css +2 -2
  65. package/src/components/index.ts +2 -6
  66. package/src/components/layout/AppContent.vue +5 -19
  67. package/src/components/layout/AppLayout.vue +47 -18
  68. package/src/components/layout/AppSidebar.vue +16 -33
  69. package/src/components/layout/Resizable.vue +5 -2
  70. package/src/components/layout/TabsNav.vue +5 -5
  71. package/src/components/layout/appLayoutContext.ts +44 -0
  72. package/src/components/layout/index.ts +2 -0
  73. package/src/components/lightbox/Lightbox.vue +3 -9
  74. package/src/composables/index.ts +1 -0
  75. package/src/composables/useDevice.ts +2 -1
  76. package/src/composables/useEscapeKey.ts +56 -0
  77. package/src/composables/useSchemaField.ts +2 -17
  78. package/src/composables/useTheme.ts +23 -19
  79. package/src/form-flow/FormFlow.vue +2 -0
  80. package/src/form-flow/form-flow.ts +7 -0
  81. package/src/index.ts +0 -2
  82. package/src/styles/inputs.css +1 -1
  83. package/src/types/BagelForm.ts +46 -151
  84. package/src/types/BtnOptions.ts +5 -3
  85. package/src/utils/constants.ts +7 -0
  86. package/src/utils/index.ts +19 -3
  87. package/src/utils/sizeParsing.ts +5 -5
  88. package/vite.config.ts +5 -1
  89. package/src/components/Carousel.vue +0 -724
  90. package/src/components/ImportData.vue +0 -1749
  91. package/src/components/Pill.vue +0 -150
  92. package/src/components/Slider.vue +0 -1446
  93. package/src/components/Title.vue +0 -23
  94. package/src/components/ToolBar.vue +0 -9
  95. package/src/components/form/BagelForm.vue +0 -219
  96. package/src/components/form/BglFieldSet.vue +0 -14
  97. package/src/components/form/BglMultiStepForm.vue +0 -469
  98. package/src/components/form/FieldArray.vue +0 -422
  99. package/src/components/form/useBagelFormState.ts +0 -76
  100. package/src/composables/useFormField.ts +0 -38
  101. package/src/dialog/DialogOLD.vue +0 -358
  102. package/src/utils/BagelFormUtils.ts +0 -684
@@ -17,7 +17,7 @@ const props = defineProps<{
17
17
  iconSize?: number | string
18
18
  iconMobileSize?: number | string
19
19
  thin?: boolean
20
- size?: 'xs' | 's' | 'm' | 'l' | 'xl' | 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large'
20
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'
21
21
  fullWidth?: boolean
22
22
  fullWidthMobile?: boolean
23
23
  alignTxt?: 'center' | 'start' | 'end'
@@ -146,10 +146,10 @@ onBeforeUnmount(() => {
146
146
  { active: isActive(tab) },
147
147
  {
148
148
  'bgl_tab-thin': thin,
149
- 'bgl_tab-xs': size === 'xs' || size === 'extra-small',
150
- 'bgl_tab-s': size === 's' || size === 'small',
151
- 'bgl_tab-l': size === 'l' || size === 'large',
152
- 'bgl_tab-xl': size === 'xl' || size === 'extra-large',
149
+ 'bgl_tab-xs': size === 'xs',
150
+ 'bgl_tab-s': size === 'sm',
151
+ 'bgl_tab-l': size === 'lg',
152
+ 'bgl_tab-xl': size === 'xl',
153
153
  'w-100p justify-center': fullWidth,
154
154
  'w-auto m_w-100p': fullWidthMobile,
155
155
  'bgl_tab-align-center': alignTxt === 'center',
@@ -0,0 +1,44 @@
1
+ import type { InjectionKey, Ref } from 'vue'
2
+ import { inject, ref } from 'vue'
3
+
4
+ export const SIDEBAR_COLLAPSED_WIDTH = '66px'
5
+ export const SIDEBAR_COLLAPSED_WIDTH_CARD = '82px'
6
+
7
+ export interface AppLayoutContext {
8
+ /** Sidebar open (expanded) state */
9
+ isOpen: Ref<boolean>
10
+ /** Viewport is below MOBILE_BREAKPOINT */
11
+ isMobile: Ref<boolean>
12
+ toggleMenu: () => void
13
+ /** Close the sidebar when on mobile (e.g. after navigating) */
14
+ closeOnMobile: () => void
15
+ sidebarWidth: string
16
+ sidebarCollapsedWidth: string
17
+ /** Sidebar rendered as a floating card */
18
+ sidebarCardStyle: boolean
19
+ }
20
+
21
+ export const AppLayoutKey: InjectionKey<AppLayoutContext> = Symbol('AppLayout')
22
+
23
+ /**
24
+ * Access the AppLayout context (sidebar open state, mobile flag, toggle).
25
+ * Works inside AppLayout's slots — including custom header buttons.
26
+ * Outside an AppLayout it warns (dev) and returns an inert fallback.
27
+ */
28
+ export function useAppLayout(): AppLayoutContext {
29
+ const ctx = inject(AppLayoutKey, null)
30
+ if (ctx) { return ctx }
31
+
32
+ if (import.meta.env?.DEV) {
33
+ console.warn('[bagelink] useAppLayout() called outside <AppLayout>. AppSidebar/AppContent are designed to be used inside it.')
34
+ }
35
+ return {
36
+ isOpen: ref(true),
37
+ isMobile: ref(false),
38
+ toggleMenu: () => {},
39
+ closeOnMobile: () => {},
40
+ sidebarWidth: '260px',
41
+ sidebarCollapsedWidth: SIDEBAR_COLLAPSED_WIDTH,
42
+ sidebarCardStyle: false,
43
+ }
44
+ }
@@ -1,6 +1,8 @@
1
1
  export { default as AppContent } from './AppContent.vue'
2
2
  export { default as AppLayout } from './AppLayout.vue'
3
3
  export { default as AppSidebar } from './AppSidebar.vue'
4
+ export { useAppLayout } from './appLayoutContext'
5
+ export type { AppLayoutContext } from './appLayoutContext'
4
6
  export { default as BottomMenu } from './BottomMenu.vue'
5
7
  export { default as Layout } from './Layout.vue'
6
8
  export { default as Panel } from './Panel.vue'
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { LightboxItem } from './lightbox.types'
3
3
 
4
- import { BglVideo, Btn, Icon, Zoomer, Image, normalizeURL, Swiper, downloadFile } from '@bagelink/vue'
4
+ import { BglVideo, Btn, Icon, Zoomer, Image, normalizeURL, Swiper, downloadFile, useEscapeKey } from '@bagelink/vue'
5
5
  import { computed, ref, watch } from 'vue'
6
6
 
7
7
  const isOpen = ref(false)
@@ -31,14 +31,14 @@ function open(item: LightboxItem, groupItems?: LightboxItem[]) {
31
31
  })
32
32
  if (currentIndex.value === -1) currentIndex.value = 0
33
33
  zoom.value = 1
34
- document.addEventListener('keydown', handleKeydown)
35
34
  }
36
35
 
37
36
  function close() {
38
37
  isOpen.value = false
39
- document.removeEventListener('keydown', handleKeydown)
40
38
  }
41
39
 
40
+ useEscapeKey(close, isOpen)
41
+
42
42
  function selectItem(index: number) {
43
43
  currentIndex.value = index
44
44
  zoom.value = 1
@@ -58,12 +58,6 @@ watch(() => currentIndex.value, () => {
58
58
  zoom.value = 1
59
59
  })
60
60
 
61
- function handleKeydown(event: KeyboardEvent) {
62
- if (event.key === 'Escape') {
63
- close()
64
- }
65
- }
66
-
67
61
  defineExpose({ open, close })
68
62
  </script>
69
63
 
@@ -6,6 +6,7 @@ import { computed, toValue } from 'vue'
6
6
 
7
7
  export { useAddToCalendar } from './useAddToCalendar'
8
8
  export { useDevice } from './useDevice'
9
+ export { useEscapeKey } from './useEscapeKey'
9
10
  export { useExcel } from './useExcel'
10
11
  export { useLocalStore } from './useLocalStore'
11
12
  export { usePolling } from './usePolling'
@@ -1,4 +1,5 @@
1
1
  import { onMounted, onUnmounted, ref } from 'vue'
2
+ import { MOBILE_BREAKPOINT } from '../utils/constants'
2
3
 
3
4
  export function useDevice() {
4
5
  const innerWidth = ref(0)
@@ -20,7 +21,7 @@ export function useDevice() {
20
21
 
21
22
  // Update current values
22
23
  innerWidth.value = window.innerWidth
23
- isMobile.value = window.innerWidth < 768
24
+ isMobile.value = window.innerWidth <= MOBILE_BREAKPOINT
24
25
  scrollY.value = window.scrollY
25
26
  scrollX.value = window.scrollX
26
27
  innerHeight.value = window.innerHeight
@@ -0,0 +1,56 @@
1
+ import type { MaybeRefOrGetter } from 'vue'
2
+ import { onBeforeUnmount, onMounted, toValue } from 'vue'
3
+
4
+ interface EscapeEntry {
5
+ handler: (e: KeyboardEvent) => void
6
+ enabled: () => boolean
7
+ }
8
+
9
+ const stack: EscapeEntry[] = []
10
+ let listening = false
11
+
12
+ function onDocumentKeydown(e: KeyboardEvent) {
13
+ if (e.key !== 'Escape') { return }
14
+ // LIFO: the most recently mounted (topmost) active layer handles Escape
15
+ for (let i = stack.length - 1; i >= 0; i--) {
16
+ if (stack[i].enabled()) {
17
+ stack[i].handler(e)
18
+ return
19
+ }
20
+ }
21
+ }
22
+
23
+ function syncListener() {
24
+ if (typeof document === 'undefined') { return }
25
+ if (stack.length > 0 && !listening) {
26
+ document.addEventListener('keydown', onDocumentKeydown)
27
+ listening = true
28
+ } else if (stack.length === 0 && listening) {
29
+ document.removeEventListener('keydown', onDocumentKeydown)
30
+ listening = false
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Run a handler when Escape is pressed, while `enabled` is truthy.
36
+ * - One shared document listener for the whole app
37
+ * - LIFO: the most recently mounted active layer wins (dropdown inside a lightbox closes first)
38
+ * - Automatically cleaned up on unmount
39
+ *
40
+ * @example
41
+ * useEscapeKey(() => close(), () => isOpen.value)
42
+ */
43
+ export function useEscapeKey(handler: (e: KeyboardEvent) => void, enabled: MaybeRefOrGetter<boolean> = true) {
44
+ const entry: EscapeEntry = { handler, enabled: () => !!toValue(enabled) }
45
+
46
+ onMounted(() => {
47
+ stack.push(entry)
48
+ syncListener()
49
+ })
50
+
51
+ onBeforeUnmount(() => {
52
+ const i = stack.indexOf(entry)
53
+ if (i !== -1) { stack.splice(i, 1) }
54
+ syncListener()
55
+ })
56
+ }
@@ -4,7 +4,6 @@ import type { Field, Attributes, Path, SchemaChild, BaseBagelField, VNodeFn } fr
4
4
  import {
5
5
  TextInput,
6
6
  NumberInput,
7
- FieldArray,
8
7
  SelectInput,
9
8
  ToggleInput,
10
9
  CheckInput,
@@ -12,7 +11,6 @@ import {
12
11
  UploadInput,
13
12
  DateInput,
14
13
  TabsNav,
15
- BglForm,
16
14
  bindAttrs,
17
15
  classify,
18
16
  keyToLabel,
@@ -74,7 +72,7 @@ export function useSchemaField<T extends { [key: string]: any }>(optns: UseSchem
74
72
  text: TextInput,
75
73
  textarea: TextInput,
76
74
  number: NumberInput,
77
- array: FieldArray,
75
+ array: 'div',
78
76
  color: ColorInput,
79
77
  tel: TelInput,
80
78
  select: SelectInput,
@@ -85,7 +83,7 @@ export function useSchemaField<T extends { [key: string]: any }>(optns: UseSchem
85
83
  file: UploadInput,
86
84
  date: DateInput,
87
85
  tabs: TabsNav,
88
- form: BglForm,
86
+ form: 'div',
89
87
  range: RangeInput,
90
88
  email: EmailInput
91
89
  }
@@ -195,19 +193,6 @@ export function useSchemaField<T extends { [key: string]: any }>(optns: UseSchem
195
193
  defaultValue,
196
194
  }
197
195
 
198
- // Special handling for FieldArray component to pass attrs.schema as schema prop
199
- if (Component === FieldArray && field.attrs?.schema) {
200
- props.schema = field.attrs.schema
201
- }
202
- // Special handling for FieldArray component to pass attrs.type as type prop
203
- if (Component === FieldArray && field.attrs?.type) {
204
- props.type = field.attrs.type
205
- }
206
- // Special handling for FieldArray component to pass collapsed prop
207
- if (Component === FieldArray && 'collapsed' in field) {
208
- props.collapsed = (field as any).collapsed
209
- }
210
-
211
196
  // Wire top-level onClick with conditional args
212
197
  if (typeof (field as any).onClick === 'function') {
213
198
  const original = (field as any).onClick as (val?: any, row?: T) => void
@@ -1,5 +1,5 @@
1
1
  // composables/useTheme.ts
2
- import { ref, computed, onMounted, watch } from 'vue'
2
+ import { ref, computed, watch } from 'vue'
3
3
 
4
4
  export interface ThemeOption {
5
5
  value: string
@@ -118,33 +118,37 @@ function addTheme(theme: ThemeOption) {
118
118
  * setTheme('ocean')
119
119
  */
120
120
 
121
- export function useTheme() {
122
- onMounted(() => {
123
- const saved = window.localStorage.getItem(STORAGE_KEY)
124
- const validTheme = themeOptions.value.find(t => t.value === saved)
121
+ // Module-level one-time init: a single matchMedia listener and a single
122
+ // colorMode watcher, no matter how many components call useTheme().
123
+ let initialized = false
125
124
 
126
- if (validTheme) {
127
- colorMode.value = saved!
128
- }
125
+ function initTheme() {
126
+ if (initialized || !isBrowser) { return }
127
+ initialized = true
128
+
129
+ const saved = window.localStorage.getItem(STORAGE_KEY)
130
+ if (themeOptions.value.some(t => t.value === saved)) {
131
+ colorMode.value = saved!
132
+ }
129
133
 
130
- // Apply initial theme
131
- applyTheme(colorMode.value)
134
+ applyTheme(colorMode.value)
132
135
 
133
- // React to system changes when in "system" mode
134
- const mq = window.matchMedia('(prefers-color-scheme: dark)')
135
- const handler = (e: MediaQueryListEvent) => {
136
- if (colorMode.value === 'system') {
137
- applyTheme('system')
138
- }
136
+ // React to system changes when in "system" mode
137
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
138
+ if (colorMode.value === 'system') {
139
+ applyTheme('system')
139
140
  }
140
- mq.addEventListener('change', handler)
141
141
  })
142
142
 
143
- // Watch for mode changes
143
+ // Persist + apply on mode changes
144
144
  watch(colorMode, (newMode) => {
145
- if (isBrowser) localStorage.setItem(STORAGE_KEY, newMode)
145
+ localStorage.setItem(STORAGE_KEY, newMode)
146
146
  applyTheme(newMode)
147
147
  })
148
+ }
149
+
150
+ export function useTheme() {
151
+ initTheme()
148
152
 
149
153
  const theme = computed(() => colorMode.value)
150
154
 
@@ -10,6 +10,7 @@ import EmailInput from '../components/form/inputs/EmailInput.vue'
10
10
  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
+ import RangeInput from '../components/form/inputs/RangeInput.vue'
13
14
  import RichText from '../components/form/inputs/RichText/index.vue'
14
15
  import SelectBtn from '../components/form/inputs/SelectBtn.vue'
15
16
  import SelectInput from '../components/form/inputs/SelectInput.vue'
@@ -223,6 +224,7 @@ function getFieldComponent(fieldOrType: FieldBuilder | string) {
223
224
  array: ArrayInput,
224
225
  upload: UploadInput,
225
226
  color: ColorInput,
227
+ range: RangeInput,
226
228
  }
227
229
 
228
230
  return (componentMap[type] as typeof TextInput | undefined) ?? TextInput
@@ -358,6 +358,13 @@ export const $ = {
358
358
  return new Field('color', parseArgs(labelOrConfig, config))
359
359
  },
360
360
 
361
+ range(
362
+ labelOrConfig?: string | (BaseFieldConfig & { min?: number, max?: number, step?: number, multiRange?: boolean }),
363
+ config?: BaseFieldConfig & { min?: number, max?: number, step?: number, multiRange?: boolean }
364
+ ): FieldBuilder<number | [number, number]> {
365
+ return new Field('range', parseArgs(labelOrConfig, config))
366
+ },
367
+
361
368
  upload(
362
369
  labelOrConfig?: string | (BaseFieldConfig & {
363
370
  multiple?: boolean
package/src/index.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import './styles/bagel.css'
2
2
 
3
3
  export * from './components'
4
- export * from './components/form/useBagelFormState'
5
4
  export * from './composables'
6
5
  // Dialog (native <dialog> based)
7
6
  export * from './dialog'
@@ -29,7 +28,6 @@ export { type BagelToastOptions, type ToastApi, ToastPlugin, useToast } from './
29
28
  export * from './types'
30
29
  export * from './utils'
31
30
  export * from './utils/allCountries'
32
- export * from './utils/BagelFormUtils'
33
31
  export * from './utils/calendar/dateUtils'
34
32
 
35
33
  export * from './utils/constants'
@@ -524,7 +524,7 @@ select {
524
524
  }
525
525
 
526
526
  .error-message {
527
- color: var(--bgl-red, #dc3545);
527
+ color: var(--bgl-red);
528
528
  font-size: 10px;
529
529
  position: absolute;
530
530
  inset-inline-end: 0;
@@ -1,83 +1,30 @@
1
- // Local type definitions for internal use only
2
- import type { Paths, Get, IterableElement, OmitIndexSignature } from 'type-fest'
1
+ /**
2
+ * Schema field types used by DataTable, DataPreview, useSchemaField and elementUtils.
3
+ * (The BagelForm form-rendering system was removed — use FormFlow with defineSchema.)
4
+ */
5
+ import type { Paths, Get, OmitIndexSignature } from 'type-fest'
3
6
  import type { ToString } from 'type-fest/source/internal'
4
7
  import type { LiteralStringUnion } from 'type-fest/source/literal-union'
5
8
  import type { PathsOptions, DefaultPathsOptions } from 'type-fest/source/paths'
6
9
  import type { VNode } from 'vue'
7
10
 
8
- type ArrayAttrs = any
9
- interface Option { label: string, value: any }
10
-
11
11
  // BagelInputShellProps is exported from components/form/inputs/bagelInputShell.ts
12
12
  // Do not re-export here to avoid duplicate export in src/index.ts
13
13
  import type { BagelInputShellProps } from '../components/form/inputs/bagelInputShell'
14
14
 
15
- export type AttributeValue
16
- = | string
17
- | number
18
- | boolean
19
- | undefined
20
- | { [key: string]: any }
21
-
22
- export type AttributeFn<T, P extends Path<T>> = (
23
- field: SmartFieldVal<T, P>,
24
- row?: T
25
- ) => AttributeValue
26
-
27
- export interface Attributes<T, P extends Path<T>> {
28
- [key: string]: AttributeValue | AttributeFn<T, P>
29
- }
30
-
31
- export type BagelFieldOptions<T, P extends Path<T>>
32
- = | string
33
- | (
34
- | {
35
- label?: string
36
- value: string | number
37
- }
38
- | string
39
- | number
40
- | boolean
41
- | { [key: string]: any }
42
- )[]
43
- | ((val?: SmartFieldVal<T, P>, rowData?: T) => Option[] | ((query: string) => Promise<Option[]>))
44
- | ((query: string, val?: SmartFieldVal<T, P>, rowData?: T) => Promise<Option[]>)
45
-
46
- export type VIfType<T, P extends Path<T>>
47
- = | string
48
- | boolean
49
- | ((val?: SmartFieldVal<T, P>, rowData?: T) => boolean)
50
-
51
- export type ValidationFn<T, P extends Path<T>> = (
52
- val?: SmartFieldVal<T, P>,
53
- rowData?: T
54
- ) => string | undefined
15
+ interface Option { label: string, value: any }
55
16
 
56
- export interface ShallowPathsOptions extends PathsOptions {
57
- maxRecursionDepth: 0
58
- }
17
+ // ---------- Path resolution ----------
59
18
 
60
19
  export type _Path<
61
20
  T,
62
21
  PO extends PathsOptions = DefaultPathsOptions,
63
22
  > = ToString<Paths<OmitIndexSignature<T>, PO>>
64
23
 
65
- // Utility type for open-ended object paths
66
- export type OpenEndedPath<T>
67
- = keyof T extends infer K
68
- ? K extends keyof T
69
- ? T[K] extends { [key: string]: any }
70
- ? `${K & string}.${string}`
71
- : never
72
- : never
73
- : never
74
-
75
24
  // Helper type to get paths for index signature properties
76
25
  type IndexSignaturePaths<T> = {
77
26
  [K in keyof T]: T[K] extends { [key: string]: any }
78
- ? T[K] extends { [key: string]: infer V }
79
- ? `${K & string}.${string}`
80
- : never
27
+ ? `${K & string}.${string}`
81
28
  : never
82
29
  }[keyof T]
83
30
 
@@ -87,7 +34,11 @@ export type Path<T, PO extends PathsOptions = DefaultPathsOptions>
87
34
  ? LiteralStringUnion<_Path<T, PO>>
88
35
  : _Path<T, PO> | IndexSignaturePaths<T> | `${keyof T & string}.more_info.${string}`
89
36
 
90
- // Smart field value type that preserves type information when possible
37
+ /** The value type at path P in object T. Falls back to unknown if resolution fails. */
38
+ export type FieldVal<T, P extends Path<T>>
39
+ = unknown extends Get<T, P> ? unknown : Get<T, P>
40
+
41
+ /** Field value type that preserves type information when possible. */
91
42
  export type SmartFieldVal<T, P extends Path<T>>
92
43
  = P extends string
93
44
  ? P extends keyof T
@@ -95,8 +46,25 @@ export type SmartFieldVal<T, P extends Path<T>>
95
46
  : any
96
47
  : FieldVal<T, P>
97
48
 
98
- // Smart bagel field options that preserve type information
99
- export type SmartBagelFieldOptions<T, P extends Path<T>>
49
+ // ---------- Field callbacks & attributes ----------
50
+
51
+ export type AttributeValue
52
+ = | string
53
+ | number
54
+ | boolean
55
+ | undefined
56
+ | { [key: string]: any }
57
+
58
+ export type AttributeFn<T, P extends Path<T>> = (
59
+ field: SmartFieldVal<T, P>,
60
+ row?: T
61
+ ) => AttributeValue
62
+
63
+ export interface Attributes<T, P extends Path<T>> {
64
+ [key: string]: AttributeValue | AttributeFn<T, P>
65
+ }
66
+
67
+ export type FieldOptions<T, P extends Path<T>>
100
68
  = | string
101
69
  | (
102
70
  | {
@@ -111,43 +79,27 @@ export type SmartBagelFieldOptions<T, P extends Path<T>>
111
79
  | ((val?: SmartFieldVal<T, P>, rowData?: T) => Option[] | ((query: string) => Promise<Option[]>))
112
80
  | ((query: string, val?: SmartFieldVal<T, P>, rowData?: T) => Promise<Option[]>)
113
81
 
114
- // Smart VIf type that preserves type information
115
- export type SmartVIfType<T, P extends Path<T>>
82
+ export type VIfType<T, P extends Path<T>>
116
83
  = | string
117
84
  | boolean
118
85
  | ((val?: SmartFieldVal<T, P>, rowData?: T) => boolean)
119
86
 
120
- // Smart validation function that preserves type information
121
- export type SmartValidationFn<T, P extends Path<T>> = (
87
+ export type ValidationFn<T, P extends Path<T>> = (
122
88
  val?: SmartFieldVal<T, P>,
123
89
  rowData?: T
124
90
  ) => string | undefined
125
91
 
126
- // Smart attribute function that preserves type information
127
- export type SmartAttributeFn<T, P extends Path<T>> = (
128
- field: SmartFieldVal<T, P>,
129
- row?: T
130
- ) => AttributeValue
131
-
132
- // Smart transform function that preserves type information
133
- export type SmartTransformFn<T, P extends Path<T>> = (
92
+ export type TransformFn<T, P extends Path<T>> = (
134
93
  val?: SmartFieldVal<T, P>,
135
94
  rowData?: T
136
95
  ) => any
137
96
 
138
- // Smart update function that preserves type information
139
- export type SmartUpdateFn<T, P extends Path<T>> = (
97
+ export type UpdateFn<T, P extends Path<T>> = (
140
98
  val?: SmartFieldVal<T, P>,
141
99
  rowData?: T
142
100
  ) => unknown
143
101
 
144
- /** The value type at path P in object T. */
145
- // Fall back to unknown if type resolution fails
146
- export type FieldVal<T, P extends Path<T>>
147
- = unknown extends Get<T, P> ? unknown : Get<T, P>
148
-
149
- /** If path P in T is an array, this gives the array's element type. */
150
- export type ArrayFieldVal<T, P extends Path<T>> = IterableElement<Get<T, P>>
102
+ // ---------- Schema fields ----------
151
103
 
152
104
  export type VNodeFn<T, P extends Path<T>> = (props: {
153
105
  row?: T
@@ -189,50 +141,26 @@ export interface BaseBagelField<
189
141
  'id'?: P
190
142
  'label'?: string
191
143
  'placeholder'?: string
192
- 'class'?: AttributeValue | SmartAttributeFn<T, P>
144
+ 'class'?: AttributeValue | AttributeFn<T, P>
193
145
  'attrs'?: Attributes<T, P>
194
146
  'required'?: boolean
195
147
  'disabled'?: boolean
196
148
  'helptext'?: string
197
- 'options'?: SmartBagelFieldOptions<T, P>
149
+ 'options'?: FieldOptions<T, P>
198
150
  'children'?: SchemaChild<T, Path<T, PO>, PO>[]
199
151
  'slots'?: { [key: string]: SchemaChild<T, Path<T, PO>, PO>[] }
200
152
  'defaultValue'?: any
201
- 'vIf'?: SmartVIfType<T, P>
202
- 'v-if'?: SmartVIfType<T, P>
203
- 'transform'?: SmartTransformFn<T, P>
204
- 'onUpdate'?: SmartUpdateFn<T, P>
205
- 'validate'?: SmartValidationFn<T, P>
153
+ 'vIf'?: VIfType<T, P>
154
+ 'v-if'?: VIfType<T, P>
155
+ 'transform'?: TransformFn<T, P>
156
+ 'onUpdate'?: UpdateFn<T, P>
157
+ 'validate'?: ValidationFn<T, P>
206
158
  }
207
159
 
208
- export type _MappedBaseBagelField<
209
- T,
210
- PO extends PathsOptions = DefaultPathsOptions,
211
- > = {
212
- [P in Path<T, PO>]: BaseBagelField<T, P, PO>
213
- }
214
-
215
- export type MappedBaseBagelFieldP<
216
- T,
217
- P extends Path<T, PO>,
218
- PO extends PathsOptions = DefaultPathsOptions,
219
- > = _MappedBaseBagelField<T, PO>[P]
220
-
221
- export type FieldByP<
222
- T,
223
- P extends Path<T, PO>,
224
- PO extends PathsOptions = DefaultPathsOptions,
225
- > = MappedBaseBagelFieldP<T, P, PO>
226
-
227
160
  export type Field<
228
161
  T,
229
162
  PO extends PathsOptions = DefaultPathsOptions,
230
- > = MappedBaseBagelFieldP<T, Path<T, PO>, PO>
231
-
232
- export type BglFieldT<
233
- T,
234
- PO extends PathsOptions = DefaultPathsOptions,
235
- > = Field<T, PO>
163
+ > = ValidBaseBagelField<T, PO>
236
164
 
237
165
  export type SchemaField<
238
166
  T,
@@ -244,40 +172,7 @@ export type BglFormSchemaT<
244
172
  PO extends PathsOptions = DefaultPathsOptions,
245
173
  > = (SchemaField<T, PO> | BaseBagelField<T, Path<T, PO>, PO>)[]
246
174
 
247
- export type ShallowBglFormSchemaT<
248
- T,
249
- PO extends PathsOptions = ShallowPathsOptions,
250
- > = (SchemaField<T, PO> | BaseBagelField<T, Path<T, PO>, PO>)[]
251
-
252
- export interface InputBagelField<
253
- T,
254
- P extends Path<T, PO>,
255
- PO extends PathsOptions = DefaultPathsOptions
256
- >
257
- extends BaseBagelField<T, P, PO> {
258
- $el: 'text' | any
259
- type?: string
260
- }
261
-
262
- export interface SelectBagelField<
263
- T,
264
- P extends Path<T, PO>,
265
- PO extends PathsOptions = DefaultPathsOptions,
266
- >
267
- extends BaseBagelField<T, P, PO> {
268
- $el: 'select' | any
269
- type?: string
270
- }
271
-
272
- export interface ArrayBagelField<
273
- T,
274
- P extends Path<T, PO>,
275
- PO extends PathsOptions = DefaultPathsOptions,
276
- >
277
- extends BaseBagelField<T, P, PO> {
278
- $el: 'array' | any
279
- attrs?: ArrayAttrs
280
- }
175
+ // ---------- Input component contracts ----------
281
176
 
282
177
  export interface ValidateInputBaseT {
283
178
  validate?: ValidationFn<{ [key: string]: unknown }, string>
@@ -3,18 +3,20 @@ import type { IconType, ThemeType } from '.'
3
3
  export interface BtnOptions {
4
4
  onClick?: () => void
5
5
  color?: ThemeType
6
- theme?: ThemeType
7
6
  disabled?: boolean
8
7
  icon?: IconType
8
+ variant?: 'solid' | 'flat' | 'outline'
9
9
  flat?: boolean
10
+ outline?: boolean
11
+ /** @deprecated Use `outline` */
12
+ border?: boolean
10
13
  thin?: boolean
11
14
  type?: 'button' | 'submit' | 'reset'
12
15
  loading?: boolean
13
16
  role?: string
14
17
  value?: string
15
- border?: boolean
16
18
  }
17
19
 
18
20
  export type UnitSize = 'px' | 'rem' | 'vh' | 'vw'
19
- export type SizeType = 'xSmall' | 'small' | 'medium' | 'large' | 'xLarge'
21
+ export type SizeType = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
20
22
  export type SizeUnit = `${number}` | number | `${number}${UnitSize}`