@bagelink/vue 1.1.33 → 1.1.35-beta.2

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 (84) hide show
  1. package/dist/components/DataPreview.vue.d.ts +3 -1
  2. package/dist/components/DataPreview.vue.d.ts.map +1 -1
  3. package/dist/components/Spreadsheet/Index.vue.d.ts +5 -61
  4. package/dist/components/Spreadsheet/Index.vue.d.ts.map +1 -1
  5. package/dist/components/Spreadsheet/SpreadsheetTable.vue.d.ts +3 -1
  6. package/dist/components/Spreadsheet/SpreadsheetTable.vue.d.ts.map +1 -1
  7. package/dist/components/dataTable/DataTable.vue.d.ts +3 -1
  8. package/dist/components/dataTable/DataTable.vue.d.ts.map +1 -1
  9. package/dist/components/dataTable/tableTypes.d.ts +5 -1
  10. package/dist/components/dataTable/tableTypes.d.ts.map +1 -1
  11. package/dist/components/dataTable/useTableData.d.ts +7 -3
  12. package/dist/components/dataTable/useTableData.d.ts.map +1 -1
  13. package/dist/components/dataTable/useTableSelection.d.ts +3 -1
  14. package/dist/components/dataTable/useTableSelection.d.ts.map +1 -1
  15. package/dist/components/form/BagelForm.vue.d.ts +1 -1
  16. package/dist/components/form/BagelForm.vue.d.ts.map +1 -1
  17. package/dist/components/form/inputs/DatePick.vue.d.ts.map +1 -1
  18. package/dist/components/form/inputs/PhoneInput.vue.d.ts +2 -60
  19. package/dist/components/form/inputs/PhoneInput.vue.d.ts.map +1 -1
  20. package/dist/components/form/inputs/RadioGroup.vue.d.ts +3 -1
  21. package/dist/components/form/inputs/RadioGroup.vue.d.ts.map +1 -1
  22. package/dist/components/form/inputs/RichText/utils/debug.d.ts +3 -1
  23. package/dist/components/form/inputs/RichText/utils/debug.d.ts.map +1 -1
  24. package/dist/components/form/inputs/RichText/utils/media.d.ts +10 -4
  25. package/dist/components/form/inputs/RichText/utils/media.d.ts.map +1 -1
  26. package/dist/components/form/inputs/SelectInput.vue.d.ts +4 -120
  27. package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
  28. package/dist/components/form/inputs/TextInput.vue.d.ts +5 -5
  29. package/dist/components/form/inputs/TextInput.vue.d.ts.map +1 -1
  30. package/dist/components/form/useBagelFormState.d.ts +1 -1
  31. package/dist/components/form/useBagelFormState.d.ts.map +1 -1
  32. package/dist/composables/index.d.ts +1 -0
  33. package/dist/composables/index.d.ts.map +1 -1
  34. package/dist/composables/useFormField.d.ts +1 -1
  35. package/dist/composables/useFormField.d.ts.map +1 -1
  36. package/dist/composables/useSchemaField.d.ts +1 -1
  37. package/dist/composables/useSchemaField.d.ts.map +1 -1
  38. package/dist/composables/useValidateFieldValue.d.ts +4 -0
  39. package/dist/composables/useValidateFieldValue.d.ts.map +1 -0
  40. package/dist/index.cjs +78 -19
  41. package/dist/index.mjs +78 -19
  42. package/dist/plugins/modal.d.ts +4 -2
  43. package/dist/plugins/modal.d.ts.map +1 -1
  44. package/dist/plugins/modalTypes.d.ts +6 -4
  45. package/dist/plugins/modalTypes.d.ts.map +1 -1
  46. package/dist/style.css +162 -150
  47. package/dist/types/BagelForm.d.ts +22 -10
  48. package/dist/types/BagelForm.d.ts.map +1 -1
  49. package/dist/types/TableSchema.d.ts +5 -1
  50. package/dist/types/TableSchema.d.ts.map +1 -1
  51. package/dist/utils/BagelFormUtils.d.ts +21 -50
  52. package/dist/utils/BagelFormUtils.d.ts.map +1 -1
  53. package/dist/utils/index.d.ts +1 -1
  54. package/dist/utils/index.d.ts.map +1 -1
  55. package/package.json +1 -1
  56. package/src/components/Btn.vue +1 -1
  57. package/src/components/DataPreview.vue +2 -2
  58. package/src/components/ListItem.vue +1 -1
  59. package/src/components/Spreadsheet/Index.vue +10 -10
  60. package/src/components/Spreadsheet/SpreadsheetTable.vue +1 -1
  61. package/src/components/calendar/language/index.ts +1 -1
  62. package/src/components/dataTable/DataTable.vue +2 -2
  63. package/src/components/dataTable/tableTypes.ts +1 -1
  64. package/src/components/dataTable/useTableData.ts +4 -4
  65. package/src/components/dataTable/useTableSelection.ts +1 -1
  66. package/src/components/form/BagelForm.vue +4 -4
  67. package/src/components/form/inputs/DatePick.vue +0 -1
  68. package/src/components/form/inputs/RadioGroup.vue +1 -1
  69. package/src/components/form/inputs/RichText/utils/debug.ts +1 -1
  70. package/src/components/form/inputs/RichText/utils/media.ts +19 -10
  71. package/src/components/form/inputs/TextInput.vue +39 -33
  72. package/src/components/form/useBagelFormState.ts +1 -1
  73. package/src/composables/index.ts +1 -0
  74. package/src/composables/useFormField.ts +1 -1
  75. package/src/composables/useSchemaField.ts +11 -6
  76. package/src/composables/useValidateFieldValue.ts +26 -0
  77. package/src/plugins/modal.ts +6 -5
  78. package/src/plugins/modalTypes.ts +4 -4
  79. package/src/styles/appearance.css +16 -0
  80. package/src/styles/buttons.css +2 -2
  81. package/src/types/BagelForm.ts +33 -11
  82. package/src/types/TableSchema.ts +1 -1
  83. package/src/utils/BagelFormUtils.ts +23 -22
  84. package/src/utils/index.ts +1 -1
@@ -1,14 +1,20 @@
1
- import type { Modal } from '@bagelink/vue'
1
+ import type { Field } from '@bagelink/vue'
2
+ import type { ModalApi } from '../../../../../plugins/modal'
3
+
2
4
  import type { EditorState } from '../richTextTypes'
3
5
  import { bagelFormUtils as bglFrmUtil } from '@bagelink/vue'
4
6
 
5
- // const { frmRow, numField } = bagelFormUtils
6
-
7
- export function insertImage(modal: typeof Modal, state: EditorState) {
7
+ export function insertImage(modal: ModalApi, state: EditorState) {
8
8
  const { range, doc } = state
9
9
  if (!range || !doc) return
10
10
 
11
- modal.showModalForm({
11
+ modal.showModalForm<{
12
+ src: string
13
+ alt: string
14
+ width: number
15
+ height: number
16
+ figcaption: boolean
17
+ }>({
12
18
  title: 'Upload Image',
13
19
  schema: [
14
20
  { id: 'src', $el: 'file', attrs: { bindkey: 'url' } },
@@ -19,7 +25,7 @@ export function insertImage(modal: typeof Modal, state: EditorState) {
19
25
  ),
20
26
  { id: 'figcaption', $el: 'check', label: 'Show Caption' },
21
27
  ],
22
- onSubmit: (data: Record<string, any>) => {
28
+ onSubmit: (data: { [key: string]: any }) => {
23
29
  if (data.src) {
24
30
  const img = doc.createElement('img')
25
31
  Object.assign(img, {
@@ -47,7 +53,7 @@ export function insertImage(modal: typeof Modal, state: EditorState) {
47
53
  })
48
54
  }
49
55
 
50
- export function insertLink(modal: typeof Modal, state: EditorState) {
56
+ export function insertLink(modal: ModalApi, state: EditorState) {
51
57
  const { range, doc } = state
52
58
  if (!range || !doc) return
53
59
 
@@ -69,11 +75,14 @@ export function insertLink(modal: typeof Modal, state: EditorState) {
69
75
  })
70
76
  }
71
77
 
72
- export function insertEmbed(modal: typeof Modal, state: EditorState) {
78
+ export interface InsertImbedModalData { url: string, width?: number, height?: number, allowFullscreen?: boolean }
79
+
80
+ export function insertEmbed(modal: ModalApi, state: EditorState) {
73
81
  const { range, doc } = state
74
82
  if (!range || !doc) return
75
83
 
76
- modal.showModalForm({
84
+ modal.showModalForm<InsertImbedModalData
85
+ >({
77
86
  title: 'Insert Embed',
78
87
  schema: [
79
88
  { id: 'url', $el: 'text', label: 'URL', attrs: { placeholder: 'Enter URL (YouTube, Vimeo, etc.)' } },
@@ -81,7 +90,7 @@ export function insertEmbed(modal: typeof Modal, state: EditorState) {
81
90
  bglFrmUtil.numField('width', 'Width', { min: 200, placeholder: '560' }),
82
91
  bglFrmUtil.numField('height', 'Height', { min: 200, placeholder: '315' })
83
92
  ),
84
- { id: 'allowFullscreen', $el: 'check', label: 'Allow Fullscreen', value: true },
93
+ { id: 'allowFullscreen', $el: 'check', label: 'Allow Fullscreen', value: true } as Field<InsertImbedModalData>,
85
94
  ],
86
95
  onSubmit: (data: { url: string, width?: number, height?: number, allowFullscreen?: boolean }) => {
87
96
  if (!data.url) return
@@ -1,41 +1,40 @@
1
1
  <script setup lang="ts">
2
- import type { IconType } from '@bagelink/vue'
3
- import {
4
- Icon,
5
- useDebounceFn
2
+ import type { IconType, ValidateInputBaseT } from '@bagelink/vue'
3
+ import { Icon, useDebounceFn, useValidateFieldValue } from '@bagelink/vue'
6
4
 
7
- } from '@bagelink/vue'
8
5
  import { onMounted, watch } from 'vue'
9
6
 
7
+ export interface TextInputProps extends ValidateInputBaseT {
8
+ id?: string
9
+ title?: string
10
+ helptext?: string
11
+ name?: string
12
+ placeholder?: string
13
+ modelValue?: string | number
14
+ label?: string
15
+ small?: boolean
16
+ dense?: boolean
17
+ required?: boolean
18
+ pattern?: string
19
+ defaultValue?: string | number
20
+ shrink?: boolean
21
+ disabled?: boolean
22
+ type?: string
23
+ nativeInputAttrs?: { [key: string]: any }
24
+ icon?: IconType
25
+ iconStart?: IconType
26
+ multiline?: boolean
27
+ autoheight?: boolean
28
+ code?: boolean
29
+ rows?: number | string
30
+ autocomplete?: AutoFillField
31
+ autofocus?: boolean
32
+ onFocusout?: (e: FocusEvent) => void
33
+ onFocus?: (e: FocusEvent) => void
34
+ }
35
+
10
36
  const props = withDefaults(
11
- defineProps<{
12
- id?: string
13
- title?: string
14
- helptext?: string
15
- name?: string
16
- placeholder?: string
17
- modelValue?: string | number
18
- label?: string
19
- small?: boolean
20
- dense?: boolean
21
- required?: boolean
22
- pattern?: string
23
- defaultValue?: string | number
24
- shrink?: boolean
25
- disabled?: boolean
26
- type?: string
27
- nativeInputAttrs?: { [key: string]: any }
28
- icon?: IconType
29
- iconStart?: IconType
30
- multiline?: boolean
31
- autoheight?: boolean
32
- code?: boolean
33
- rows?: number | string
34
- autocomplete?: AutoFillField
35
- autofocus?: boolean
36
- onFocusout?: (e: FocusEvent) => void
37
- onFocus?: (e: FocusEvent) => void
38
- }>(),
37
+ defineProps<TextInputProps>(),
39
38
  {
40
39
  type: 'text',
41
40
  modelValue: '',
@@ -46,6 +45,13 @@ let inputVal = $ref<string | number>()
46
45
 
47
46
  const input = $ref<HTMLInputElement>()
48
47
 
48
+ useValidateFieldValue(
49
+ () => inputVal,
50
+ () => input,
51
+ props.validate,
52
+ props.getFormData
53
+ )
54
+
49
55
  const inputRows = $computed(() => {
50
56
  let rows = Number(props.rows) || 1
51
57
  if (props.autoheight) rows = Math.max(rows, String(inputVal).split('\n').length)
@@ -4,7 +4,7 @@ import { getNestedValue } from '../../utils'
4
4
 
5
5
  export const FORM_STATE_KEY = Symbol('bagelFormState')
6
6
 
7
- export interface BagelFormState<T = any> {
7
+ export interface BagelFormState<T> {
8
8
  data: Ref<T>
9
9
  getFieldData: (path?: string) => any
10
10
  updateField: (path: string, value: any) => void
@@ -5,6 +5,7 @@ import { getFallbackSchema } from '@bagelink/vue'
5
5
  import { ref, watch } from 'vue'
6
6
 
7
7
  export { useDevice } from './useDevice'
8
+ export { useValidateFieldValue } from './useValidateFieldValue'
8
9
 
9
10
  interface UseBglSchemaParamsT<T> {
10
11
  schema?: BglFormSchemaFnT<T>
@@ -3,7 +3,7 @@ import type { BagelFormState } from '../components/form/useBagelFormState'
3
3
  import { inject, computed } from 'vue'
4
4
  import { FORM_STATE_KEY } from '../components/form/useBagelFormState'
5
5
 
6
- export function useFormField<T = any>(props: {
6
+ export function useFormField<T>(props: {
7
7
  field: Field<T>
8
8
  fieldID?: string
9
9
  modelValue?: any
@@ -28,7 +28,7 @@ const SRC_VALUE_COMPONENTS = new Set(['img', 'iframe'])
28
28
 
29
29
  export interface UseSchemaFieldOptions<T> {
30
30
  mode?: 'form' | 'preview' | 'table'
31
- getRowData?: () => T
31
+ getFormData?: () => T
32
32
  onUpdate?: (field: BaseBagelField<T>, value: any) => void
33
33
  includeUnset?: boolean
34
34
  }
@@ -40,7 +40,7 @@ type SlotFunction<T> = (props: SlotProps<T>) => any
40
40
  type SupportedSlot<T> = BglFormSchemaT<T> | VNode | SlotFunction<T>
41
41
 
42
42
  export function useSchemaField<T extends { [key: string]: any }>(optns: UseSchemaFieldOptions<T>) {
43
- const { mode = 'form', getRowData, onUpdate, includeUnset = false } = optns
43
+ const { mode = 'form', getFormData, onUpdate, includeUnset = false } = optns
44
44
 
45
45
  // Helper function to render objects recursively
46
46
  function renderObject(obj: any, depth = 0): string {
@@ -113,13 +113,13 @@ export function useSchemaField<T extends { [key: string]: any }>(optns: UseSchem
113
113
  const Component = getComponent(field)
114
114
  if (!Component) return null
115
115
 
116
- const rowData = getRowData?.() || {} as T
116
+ const rowData = getFormData?.() || {} as T
117
117
 
118
118
  // Check vIf condition first
119
119
  const condition = field.vIf ?? field['v-if']
120
120
  if (condition !== undefined) {
121
121
  if (typeof condition === 'function') {
122
- if (!condition(field.id ? rowData[field.id as keyof T] : undefined, rowData)) {
122
+ if (!condition(field.id ? rowData[field.id] : undefined, rowData)) {
123
123
  return null
124
124
  }
125
125
  } else if (typeof condition === 'string') {
@@ -139,6 +139,7 @@ export function useSchemaField<T extends { [key: string]: any }>(optns: UseSchem
139
139
  class: fieldClass,
140
140
  id,
141
141
  transform,
142
+ validate,
142
143
  slots: fieldSlots,
143
144
  required,
144
145
  label,
@@ -151,8 +152,9 @@ export function useSchemaField<T extends { [key: string]: any }>(optns: UseSchem
151
152
  const currentValue = field.id
152
153
  ? ('get' in rowData
153
154
  ? rowData.get(field.id)
154
- : rowData[field.id as keyof T])
155
+ : rowData[field.id])
155
156
  : undefined
157
+
156
158
  const transformedValue = transform ? transform(currentValue, rowData) : currentValue
157
159
 
158
160
  // First bind any function attributes with the current value and row data
@@ -169,6 +171,9 @@ export function useSchemaField<T extends { [key: string]: any }>(optns: UseSchem
169
171
  label,
170
172
  placeholder,
171
173
  disabled,
174
+ validate,
175
+ id,
176
+ getFormData,
172
177
  }
173
178
 
174
179
  // For form mode, always use the original value for modelValue
@@ -218,7 +223,7 @@ export function useSchemaField<T extends { [key: string]: any }>(optns: UseSchem
218
223
  props.class = classify(currentValue, rowData, fieldClass, props.class)
219
224
 
220
225
  // Handle component slots with vIf aware child rendering
221
- const componentSlots: Record<string, any> = {}
226
+ const componentSlots: { [key: string]: any } = {}
222
227
 
223
228
  // Add default slot if there are children
224
229
  if (children?.length) {
@@ -0,0 +1,26 @@
1
+ import type { ValidateInputBaseT } from '@bagelink/vue'
2
+ import type { WatchSource } from 'vue'
3
+ import { watchDebounced } from '@vueuse/core'
4
+
5
+ export function useValidateFieldValue(
6
+ inputVal: WatchSource,
7
+ getInput?: () => HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | undefined,
8
+ validateFn?: ValidateInputBaseT['validate'],
9
+ getFormData?: ValidateInputBaseT['getFormData'],
10
+ ) {
11
+ watchDebounced(
12
+ inputVal,
13
+ (newVal, oldVal) => {
14
+ getInput?.()?.setCustomValidity('')
15
+
16
+ console.log({ validateFn, getFormData, input: getInput })
17
+
18
+ if (!validateFn || newVal === oldVal || !newVal) return
19
+
20
+ const isValid = validateFn(newVal, getFormData?.())
21
+
22
+ if (typeof isValid === 'string') getInput?.()?.setCustomValidity(isValid)
23
+ },
24
+ { debounce: 500 },
25
+ )
26
+ }
@@ -1,10 +1,11 @@
1
1
  import type { InjectionKey, Plugin } from 'vue'
2
+ import type { ComponentProps } from 'vue-component-type-helpers'
2
3
  import type { ConfirmModalUserOptions, ModalComponentProps, ModalConfirmOptions, ModalFormComponentProps, ModalFormOptions, ModalOptions, ModalType } from './modalTypes'
3
4
  import { Modal, ModalConfirm, ModalForm } from '@bagelink/vue'
4
5
  import { defineComponent, h, inject } from 'vue'
5
6
 
6
7
  export interface ModalApi {
7
- showModal: (options: ModalOptions, slots?: { [key: string]: any }) => ModalComponentProps | undefined
8
+ showModal: <T extends { [key: string]: any }>(options: ModalOptions, slots?: { [key: string]: any }) => ModalComponentProps<T> | undefined
8
9
  showModalForm: <T extends { [key: string]: any }>(options: ModalFormOptions<T>, slots?: { [key: string]: any }) => ModalFormComponentProps<T> | undefined
9
10
  hideModal: (index?: number) => void
10
11
  confirmModal: (options: ConfirmModalUserOptions) => Promise<boolean>
@@ -20,7 +21,7 @@ export function useModal(): ModalApi {
20
21
 
21
22
  export const ModalPlugin: Plugin = {
22
23
  install: (app) => {
23
- const modalStack = $ref<ModalComponentProps[]>([])
24
+ const modalStack = $ref<ModalComponentProps<object>[]>([])
24
25
 
25
26
  const hideModal = (index: number) => {
26
27
  modalStack.splice(index, 1)
@@ -37,11 +38,11 @@ export const ModalPlugin: Plugin = {
37
38
  })
38
39
  }
39
40
 
40
- const showModal = <T extends { [key: string]: any } = any>(
41
+ const showModal = <T extends { [key: string]: any }>(
41
42
  modalType: ModalType,
42
43
  options: ModalOptions | ModalFormOptions<T>,
43
44
  slots: { [key: string]: any } = {}
44
- ): ModalComponentProps | ModalFormComponentProps<T> | undefined => {
45
+ ): ModalComponentProps<T> | ModalFormComponentProps<T> | undefined => {
45
46
  const modalComponent = {
46
47
  modalOptions: options,
47
48
  modalType,
@@ -76,7 +77,7 @@ export const ModalPlugin: Plugin = {
76
77
  }
77
78
  switch (modal.modalType) {
78
79
  case 'modalForm':
79
- return h(ModalForm, props as ModalFormOptions, modal.componentSlots)
80
+ return h(ModalForm, props as ComponentProps<typeof ModalForm>, modal.componentSlots)
80
81
  case 'confirmModal':
81
82
 
82
83
  return h(ModalConfirm, props as ModalConfirmOptions, {})
@@ -25,18 +25,18 @@ export interface ModalConfirmOptions {
25
25
  export type ModalType = 'modal' | 'modalForm' | 'confirmModal'
26
26
  export type ConfirmModalUserOptions = string | { title: string, message: string, confirmText?: string, cancelText?: string, confirmBtnColor?: string, cancelBtnColor?: string }
27
27
 
28
- export interface ModalComponentProps {
28
+ export interface ModalComponentProps<T extends { [key: string]: any }> {
29
29
  componentSlots: { [key: string]: any }
30
30
  modalType: ModalType
31
- modalOptions: ModalOptions | ModalFormOptions | ModalConfirmOptions
31
+ modalOptions: ModalOptions | ModalFormOptions<T> | ModalConfirmOptions
32
32
  }
33
33
 
34
- export interface ModalFormComponentProps<T extends { [key: string]: any } = any> extends ModalComponentProps {
34
+ export interface ModalFormComponentProps<T extends { [key: string]: any }> extends ModalComponentProps<T> {
35
35
  modalType: 'modalForm'
36
36
  modalOptions: ModalFormOptions<T>
37
37
  }
38
38
 
39
- export interface ModalFormOptions<T extends { [key: string]: any } = any> {
39
+ export interface ModalFormOptions<T extends { [key: string]: any }> {
40
40
  'schema': BglFormSchemaFnT<T>
41
41
  'modelValue'?: T
42
42
  'onUpdate:modelValue'?: (val: T) => void
@@ -717,6 +717,14 @@
717
717
  filter: drop-shadow(0 0 10px var(--bgl-black-tint));
718
718
  }
719
719
 
720
+ .border-white {
721
+ border-color: var(--bgl-white) !important;
722
+ }
723
+
724
+ .border-white-unset {
725
+ border-color: unset !important;
726
+ }
727
+
720
728
  .border-bottom {
721
729
  border-bottom: 1px solid var(--border-color)
722
730
  }
@@ -1475,6 +1483,14 @@
1475
1483
  box-shadow: 0 1px 5px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1) !important;
1476
1484
  }
1477
1485
 
1486
+ .m_border-white {
1487
+ border-color: var(--bgl-white) !important;
1488
+ }
1489
+
1490
+ .m_border-white-unset {
1491
+ border-color: unset !important;
1492
+ }
1493
+
1478
1494
  .m_border-bottom {
1479
1495
  border-bottom: 1px solid var(--border-color)
1480
1496
  }
@@ -94,7 +94,7 @@
94
94
  }
95
95
 
96
96
  .rotate-0 {
97
- transform: rotate(0deg);
97
+ transform: rotate(0deg) !important;
98
98
  }
99
99
 
100
100
  @media screen and (max-width: 910px) {
@@ -119,7 +119,7 @@
119
119
  }
120
120
 
121
121
  .m_rotate-0 {
122
- transform: rotate(0deg);
122
+ transform: rotate(0deg) !important;
123
123
  }
124
124
  }
125
125
 
@@ -1,11 +1,12 @@
1
1
  import type { SelectInput, TextInput } from '@bagelink/vue'
2
2
  import type { VNode } from 'vue'
3
+ import type { ComponentExposed } from 'vue-component-type-helpers'
3
4
 
4
5
  export type AttributeValue = string | number | boolean | undefined | undefined | { [key: string]: any }
5
6
 
6
- export type AttributeFn<T = { [key: string]: any }> = (field: any, row: T) => AttributeValue
7
+ export type AttributeFn<T = { [key: string]: any }> = (field: any, row?: T) => AttributeValue
7
8
 
8
- export interface Attributes<T = any> {
9
+ export interface Attributes<T> {
9
10
  [key: string]: AttributeValue | AttributeFn<T>
10
11
  }
11
12
 
@@ -24,8 +25,6 @@ export type BagelFieldOptions<T = { [key: string]: any }> = (
24
25
  | ((val: any, rowData?: T) => void)
25
26
  )
26
27
 
27
- type GenericAssertFn<T> = (val: any, row: T) => boolean
28
-
29
28
  // Helper type for controlling recursion depth
30
29
  type FiniteRangeIndex = [never, 0, 1, 2, 3, 4]
31
30
 
@@ -48,6 +47,23 @@ export type PropertyPath<T> = T extends object
48
47
  ? keyof T & string | DotNotation<T>
49
48
  : string
50
49
 
50
+ export type FieldValType<T, K> = K extends keyof T ? T[K] : never
51
+
52
+ export type GenericAssertFn<T> = (val: any, row: T) => boolean
53
+
54
+ export type TypedAssertFn<T, K> = (
55
+ K extends keyof T ?
56
+ (val: FieldValType<T, K>, row: T) => boolean :
57
+ GenericAssertFn<T>
58
+ )
59
+
60
+ export type VIfType<T, K> = string | boolean | TypedAssertFn<T, K>
61
+
62
+ export type ValidationFn<T, K> = (
63
+ val: FieldValType<T, K>,
64
+ rowData?: T,
65
+ ) => string | undefined
66
+
51
67
  export interface BaseBagelField<T = { [key: string]: any }> {
52
68
  '$el'?: any
53
69
  'id'?: PropertyPath<T>
@@ -60,22 +76,23 @@ export interface BaseBagelField<T = { [key: string]: any }> {
60
76
  'disabled'?: boolean
61
77
  'helptext'?: string
62
78
  'options'?: BagelFieldOptions<T>
63
- 'slots'?: { [key: string]: any }
79
+ 'slots'?: { [key: string]: Field<T>[] }
64
80
  'defaultValue'?: any
65
- 'transform'?: (val?: any, rowData?: T) => any
66
- 'onUpdate'?: (val: any, rowData?: T) => void
67
- 'v-if'?: string | boolean | GenericAssertFn<T>
68
- 'vIf'?: string | boolean | GenericAssertFn<T>
81
+ 'vIf'?: VIfType<T, this['id']>
82
+ 'v-if'?: VIfType<T, this['id']>
83
+ 'transform'?: (val?: FieldValType<T, this['id']>, rowData?: T) => any
84
+ 'onUpdate'?: (val: FieldValType<T, this['id']>, rowData?: T) => unknown
85
+ 'validate'?: ValidationFn<T, this['id']>
69
86
  }
70
87
 
71
88
  export interface InputBagelField<T> extends BaseBagelField<T> {
72
- $el: 'text' | InstanceType<typeof TextInput>
89
+ $el: 'text' | ComponentExposed<typeof TextInput>
73
90
  id?: PropertyPath<T>
74
91
  type?: string
75
92
  }
76
93
 
77
94
  export interface SelectBagelField<T> extends BaseBagelField<T> {
78
- $el: 'select' | InstanceType<typeof SelectInput>
95
+ $el: 'select' | ComponentExposed<typeof SelectInput>
79
96
  id?: PropertyPath<T>
80
97
  }
81
98
 
@@ -85,3 +102,8 @@ export type BglFieldT<T = { [key: string]: any }> = Field<T>
85
102
  export type BglFormSchemaT<T = { [key: string]: any }> = Field<T>[]
86
103
 
87
104
  export type BglFormSchemaFnT<T = { [key: string]: any }> = (() => BglFormSchemaT<T>) | BglFormSchemaT<T>
105
+
106
+ export interface ValidateInputBaseT {
107
+ validate?: ValidationFn<{ [key: string]: unknown }, string>
108
+ getFormData?: () => any
109
+ }
@@ -4,7 +4,7 @@ import type { Ref, ComputedRef } from 'vue'
4
4
  export type SortDirectionsT = 'ASC' | 'DESC'
5
5
  export type EmitOrderT = `${string} ${SortDirectionsT}`
6
6
 
7
- export interface TableSchemaProps<T extends Record<string, any> = Record<string, any>> {
7
+ export interface TableSchemaProps<T extends { [key: string]: any } = { [key: string]: any }> {
8
8
  data: T[]
9
9
  schema?: BglFormSchemaT<T> | (() => BglFormSchemaT<T>)
10
10
  columns?: string[]
@@ -1,7 +1,7 @@
1
- import type { BglFormSchemaT, Field, InputBagelField, Option, PropertyPath, SelectBagelField } from '@bagelink/vue'
1
+ import type { BglFormSchemaT, Field, InputBagelField, Option, PropertyPath, SelectBagelField, VIfType } from '@bagelink/vue'
2
2
  import type { UploadInputProps } from '../components/form/inputs/Upload/upload.types'
3
3
 
4
- interface InputOptions {
4
+ interface InputOptions<T, K> {
5
5
  required?: boolean
6
6
  placeholder?: string
7
7
  class?: string
@@ -9,30 +9,30 @@ interface InputOptions {
9
9
  disabled?: boolean
10
10
  helptext?: string
11
11
  autocomplete?: string
12
- vIf?: boolean | ((item: any, row: any) => boolean)
12
+ vIf?: VIfType<T, K>
13
13
  }
14
14
 
15
- interface DateOptions extends InputOptions {
15
+ interface DateOptions<T, K> extends InputOptions<T, K> {
16
16
  enableTime?: boolean
17
17
  mode?: 'day' | 'month' | 'year'
18
18
  locale?: string
19
19
  timezone?: string
20
20
  }
21
21
 
22
- interface TextInputOptions extends InputOptions {
22
+ interface TextInputOptions<T, K> extends InputOptions<T, K> {
23
23
  type?: 'text' | 'tel' | 'email'
24
24
  pattern?: string
25
25
  multiline?: boolean
26
26
  }
27
27
 
28
- interface SlctInputOptions extends InputOptions {
28
+ interface SlctInputOptions<T, K> extends InputOptions<T, K> {
29
29
  searchable?: boolean
30
30
  multiselect?: boolean
31
31
  clearable?: boolean
32
32
  onSearch?: (search: string) => any
33
33
  }
34
34
 
35
- interface NumFieldOptions extends InputOptions {
35
+ interface NumFieldOptions<T, K> extends InputOptions<T, K> {
36
36
  max?: number
37
37
  min?: number
38
38
  step?: number
@@ -42,7 +42,7 @@ interface NumFieldOptions extends InputOptions {
42
42
  useGrouping?: boolean
43
43
  }
44
44
 
45
- type RichTextOptions = InputOptions
45
+ type RichTextOptions<T, K> = InputOptions<T, K>
46
46
 
47
47
  export function getBaseField<T extends { [key: string]: any }>(
48
48
  id?: PropertyPath<T>,
@@ -56,7 +56,7 @@ export function getBaseField<T extends { [key: string]: any }>(
56
56
  export function richText<T extends { [key: string]: any }>(
57
57
  id: PropertyPath<T>,
58
58
  label?: string,
59
- options?: RichTextOptions,
59
+ options?: RichTextOptions<T, PropertyPath<T>>,
60
60
  ): Field<T> {
61
61
  return {
62
62
  $el: 'richtext',
@@ -75,7 +75,7 @@ export function richText<T extends { [key: string]: any }>(
75
75
  export function txtField<T extends { [key: string]: any }>(
76
76
  id: PropertyPath<T>,
77
77
  label?: string,
78
- options?: TextInputOptions,
78
+ options?: TextInputOptions<T, PropertyPath<T>> & Partial<Field<T>>,
79
79
  ): InputBagelField<T> {
80
80
  return {
81
81
  $el: 'text',
@@ -87,6 +87,7 @@ export function txtField<T extends { [key: string]: any }>(
87
87
  disabled: options?.disabled,
88
88
  placeholder: options?.placeholder,
89
89
  defaultValue: options?.defaultValue,
90
+ validate: options?.validate,
90
91
  attrs: {
91
92
  type: options?.type,
92
93
  pattern: options?.pattern,
@@ -100,7 +101,7 @@ export function selectField<T extends { [key: string]: any }>(
100
101
  id: PropertyPath<T>,
101
102
  label?: string,
102
103
  options?: Option[] | (() => Option[]),
103
- config?: SlctInputOptions,
104
+ config?: SlctInputOptions<T, PropertyPath<T>>,
104
105
  ): SelectBagelField<T> {
105
106
  return {
106
107
  $el: 'select',
@@ -128,7 +129,7 @@ export const slctField = selectField
128
129
  export function checkField<T extends { [key: string]: any }>(
129
130
  id: PropertyPath<T>,
130
131
  label?: string,
131
- options?: InputOptions,
132
+ options?: InputOptions<T, PropertyPath<T>>,
132
133
  ): Field<T> {
133
134
  return {
134
135
  $el: 'check',
@@ -142,7 +143,7 @@ export function checkField<T extends { [key: string]: any }>(
142
143
  export function dateField<T extends { [key: string]: any }>(
143
144
  id: PropertyPath<T>,
144
145
  label?: string,
145
- options?: DateOptions,
146
+ options?: DateOptions<T, PropertyPath<T>>,
146
147
  ): Field<T> {
147
148
  return {
148
149
  $el: 'date',
@@ -167,7 +168,7 @@ export function dateField<T extends { [key: string]: any }>(
167
168
  export function numField<T extends { [key: string]: any }>(
168
169
  id: PropertyPath<T>,
169
170
  label?: string,
170
- options?: NumFieldOptions,
171
+ options?: NumFieldOptions<T, PropertyPath<T>>,
171
172
  ): Field<T> {
172
173
  return {
173
174
  $el: 'number',
@@ -193,7 +194,7 @@ export function numField<T extends { [key: string]: any }>(
193
194
  }
194
195
  }
195
196
 
196
- export function frmRow<T>(...children: Field[]): Field<T> {
197
+ export function frmRow<T>(...children: Field<T>[]): Field<T> {
197
198
  return {
198
199
  $el: 'div',
199
200
  class: 'flex gap-1 m_block align-items-end',
@@ -201,9 +202,9 @@ export function frmRow<T>(...children: Field[]): Field<T> {
201
202
  }
202
203
  }
203
204
 
204
- export type UploadOptions = InputOptions & UploadInputProps
205
+ export type UploadOptions<T, K> = InputOptions<T, K> & UploadInputProps
205
206
 
206
- export function uploadField(id: string, label?: string, options?: UploadOptions) {
207
+ export function uploadField(id: string, label?: string, options?: UploadOptions<any, any>): Field<any> {
207
208
  return {
208
209
  $el: 'upload',
209
210
  id,
@@ -215,7 +216,7 @@ export function uploadField(id: string, label?: string, options?: UploadOptions)
215
216
  }
216
217
  }
217
218
 
218
- interface RangeOptions extends InputOptions {
219
+ interface RangeOptions<T, K> extends InputOptions<T, K> {
219
220
  min?: number
220
221
  max?: number
221
222
  step?: number
@@ -227,7 +228,7 @@ interface RangeOptions extends InputOptions {
227
228
  export function rangeField<T extends { [key: string]: any }>(
228
229
  id: PropertyPath<T>,
229
230
  label?: string,
230
- options?: RangeOptions,
231
+ options?: RangeOptions <T, PropertyPath<T>>
231
232
  ): Field<T> {
232
233
  return {
233
234
  $el: 'range',
@@ -306,7 +307,7 @@ export function findBglFieldById(id: string, _schema: BglFormSchemaT): Field | u
306
307
  return undefined
307
308
  }
308
309
 
309
- interface ArrayFieldOptions extends InputOptions {
310
+ interface ArrayFieldOptions<T, K> extends InputOptions<T, K> {
310
311
  delete?: boolean
311
312
  add?: boolean
312
313
  }
@@ -317,9 +318,9 @@ export function arrField<T extends { [key: string]: any }>(
317
318
  id: PropertyPath<T>,
318
319
  label: string,
319
320
  schemaOrType: BglFormSchemaT | ArrayType,
320
- options?: ArrayFieldOptions
321
+ options?: ArrayFieldOptions<T, PropertyPath<T>>
321
322
  ): Field<T> {
322
- const attrs: Record<string, any> = { delete: true, add: true, ...options }
323
+ const attrs: { [key: string]: any } = { delete: true, add: true, ...options }
323
324
  if (typeof schemaOrType === 'string') attrs.type = schemaOrType
324
325
  else attrs.schema = schemaOrType
325
326
  return {