@bagelink/vue 1.4.105 → 1.4.107

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 (40) hide show
  1. package/bin/generateFormSchema.ts +0 -4
  2. package/dist/components/Card.vue.d.ts +1 -0
  3. package/dist/components/Card.vue.d.ts.map +1 -1
  4. package/dist/components/Dropdown.vue.d.ts.map +1 -1
  5. package/dist/components/calendar/CalendarPopover.vue.d.ts +6 -0
  6. package/dist/components/calendar/CalendarPopover.vue.d.ts.map +1 -1
  7. package/dist/components/dataTable/DataTable.vue.d.ts +1 -1
  8. package/dist/components/dataTable/DataTable.vue.d.ts.map +1 -1
  9. package/dist/components/dataTable/useTableVirtualization.d.ts +1 -1
  10. package/dist/components/dataTable/useTableVirtualization.d.ts.map +1 -1
  11. package/dist/components/form/inputs/CodeEditor/CodeTypes.d.ts +4 -0
  12. package/dist/components/form/inputs/CodeEditor/CodeTypes.d.ts.map +1 -1
  13. package/dist/components/form/inputs/CodeEditor/Index.vue.d.ts +82 -5
  14. package/dist/components/form/inputs/CodeEditor/Index.vue.d.ts.map +1 -1
  15. package/dist/components/form/inputs/CodeEditor/useHighlight.d.ts +4 -2
  16. package/dist/components/form/inputs/CodeEditor/useHighlight.d.ts.map +1 -1
  17. package/dist/composables/useSchemaField.d.ts.map +1 -1
  18. package/dist/index.cjs +4 -4
  19. package/dist/index.mjs +20 -20
  20. package/dist/style.css +1 -1
  21. package/dist/types/BagelForm.d.ts +2 -2
  22. package/dist/types/BagelForm.d.ts.map +1 -1
  23. package/dist/utils/BagelFormUtils.d.ts +1 -0
  24. package/dist/utils/BagelFormUtils.d.ts.map +1 -1
  25. package/dist/utils/elementUtils.d.ts +8 -8
  26. package/dist/utils/elementUtils.d.ts.map +1 -1
  27. package/package.json +1 -1
  28. package/src/components/Card.vue +7 -1
  29. package/src/components/Dropdown.vue +9 -3
  30. package/src/components/dataTable/DataTable.vue +2 -9
  31. package/src/components/dataTable/useTableData.ts +1 -1
  32. package/src/components/dataTable/useTableVirtualization.ts +14 -3
  33. package/src/components/form/inputs/CodeEditor/CodeTypes.ts +260 -0
  34. package/src/components/form/inputs/CodeEditor/Index.vue +20 -30
  35. package/src/components/form/inputs/CodeEditor/useHighlight.ts +40 -3
  36. package/src/composables/useSchemaField.ts +45 -14
  37. package/src/styles/modal.css +81 -81
  38. package/src/types/BagelForm.ts +2 -2
  39. package/src/utils/BagelFormUtils.ts +1 -0
  40. package/src/utils/elementUtils.ts +51 -30
@@ -1,26 +1,27 @@
1
1
  <script setup lang="ts">
2
- import type { CodeEditorProps } from './CodeTypes'
3
- import { onMounted, ref, computed, watch } from 'vue'
2
+ import type { Language, HighlightTheme } from './CodeTypes'
3
+ import { onMounted, ref, computed, watch, type PropType } from 'vue'
4
4
  import { useHighlight } from './useHighlight'
5
5
 
6
- // Props with default values
7
- const props = withDefaults(defineProps<CodeEditorProps>(), {
8
- language: 'html',
9
- readonly: false,
10
- modelValue: '',
11
- autodetect: true,
12
- ignoreIllegals: true,
13
- label: '',
14
- height: '240px'
6
+ // Props with runtime defaults to avoid undefined at runtime
7
+ const props = defineProps({
8
+ language: { type: String as PropType<Language>, default: 'html' },
9
+ readonly: { type: Boolean, default: false },
10
+ modelValue: { type: String, default: '' },
11
+ autodetect: { type: Boolean, default: true },
12
+ ignoreIllegals: { type: Boolean, default: true },
13
+ label: { type: String, default: '' },
14
+ height: { type: String, default: '240px' },
15
+ disabled: { type: Boolean, default: false },
16
+ theme: { type: String as PropType<HighlightTheme>, default: 'dark' }
15
17
  })
16
18
 
17
19
  const emit = defineEmits(['update:modelValue'])
18
20
  // State
19
21
  const code = ref(props.modelValue || '')
20
22
  const editorRef = ref<HTMLDivElement>()
21
- const { loaded, loadHighlight, highlightCode } = useHighlight()
23
+ const { loaded, loadHighlight, highlightCode, setTheme } = useHighlight(props.theme)
22
24
 
23
- // Computed
24
25
  const maxHeight = computed(() => {
25
26
  const h = props.height ?? '240px'
26
27
  return h.match(/^\d+$/) ? `${h}px` : h
@@ -65,7 +66,7 @@ onMounted(async () => {
65
66
  await loadHighlight()
66
67
  })
67
68
 
68
- // Watch for external modelValue changes
69
+ watch(() => props.theme, (t) => { setTheme(t) })
69
70
  watch(() => props.modelValue, (newVal) => {
70
71
  if (newVal !== undefined && newVal !== code.value) {
71
72
  code.value = newVal
@@ -76,22 +77,12 @@ watch(() => props.modelValue, (newVal) => {
76
77
  <template>
77
78
  <div class="code-editor-container ltr" :style="{ maxHeight }">
78
79
  <label v-if="label" class="label">{{ label }}</label>
79
- <div
80
- v-if="loaded"
81
- ref="editorRef"
82
- class="code-editor-grandpa"
83
- >
80
+ <div v-if="loaded" ref="editorRef" class="code-editor-grandpa hljs">
84
81
  <div class="editor-content-papa relative">
85
82
  <pre class="code-display" wrap><code v-html="formattedCode" /></pre>
86
83
  <textarea
87
- v-if="!readonly"
88
- :value="code"
89
- class="code-input"
90
- spellcheck="false"
91
- autocomplete="off"
92
- autocorrect="off"
93
- autocapitalize="off"
94
- @input="handleInput"
84
+ v-if="!readonly && !disabled" :value="code" class="code-input" spellcheck="false"
85
+ autocomplete="off" autocorrect="off" autocapitalize="off" @input="handleInput"
95
86
  @keydown="handleTab"
96
87
  />
97
88
  </div>
@@ -112,7 +103,6 @@ watch(() => props.modelValue, (newVal) => {
112
103
  }
113
104
 
114
105
  .code-editor-grandpa {
115
- background: #22252A;
116
106
  border-radius: 0.25rem;
117
107
  width: 100%;
118
108
  height: 100%;
@@ -131,6 +121,7 @@ watch(() => props.modelValue, (newVal) => {
131
121
  width: 100%;
132
122
  padding-bottom: calc(100% - 5lh);
133
123
  }
124
+
134
125
  .code-display,
135
126
  .code-input {
136
127
  inset: 0;
@@ -149,7 +140,6 @@ watch(() => props.modelValue, (newVal) => {
149
140
 
150
141
  .code-display {
151
142
  position: relative;
152
- color: #fff;
153
143
  pointer-events: none;
154
144
  z-index: 1;
155
145
  }
@@ -164,7 +154,7 @@ watch(() => props.modelValue, (newVal) => {
164
154
  position: absolute;
165
155
  background: transparent;
166
156
  color: transparent;
167
- caret-color: #fff;
157
+ caret-color: #fff;
168
158
  border: none;
169
159
  resize: none;
170
160
  outline: none;
@@ -1,4 +1,4 @@
1
- import type { HighlightJS } from './CodeTypes'
1
+ import type { HighlightJS, HighlightTheme } from './CodeTypes'
2
2
  import { appendStyle, appendScript } from '@bagelink/vue'
3
3
  import { ref } from 'vue'
4
4
 
@@ -8,9 +8,44 @@ interface CustomWindow extends Window {
8
8
  }
9
9
  declare const window: CustomWindow
10
10
 
11
- export function useHighlight() {
11
+ export function useHighlight(theme: HighlightTheme = 'dark') {
12
12
  const hljs = ref<HighlightJS | null>(null)
13
13
  const loaded = ref(false)
14
+ const currentTheme = ref<HighlightTheme>(theme)
15
+
16
+ const normalizeTheme = (t: HighlightTheme): HighlightTheme => {
17
+ if (t === 'dark') return 'atom-one-dark'
18
+ if (t === 'light') return 'atom-one-light'
19
+ return t
20
+ }
21
+
22
+ const getThemeCssUrl = (t: HighlightTheme) => `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.0/styles/${t}.min.css`
23
+
24
+ const removeExistingThemeLinks = () => {
25
+ document.querySelectorAll('link[rel="stylesheet"]').forEach((link) => {
26
+ if (link instanceof HTMLLinkElement && link.href.includes('/styles/')) {
27
+ if (link.href.includes('highlight.js')) link.parentElement?.removeChild(link)
28
+ }
29
+ })
30
+ }
31
+
32
+ const setTheme = async (theme: HighlightTheme) => {
33
+ const next = normalizeTheme(theme)
34
+ if (next === currentTheme.value) return
35
+ try {
36
+ removeExistingThemeLinks()
37
+ const url = getThemeCssUrl(next)
38
+ console.log('setTheme', url)
39
+ await appendStyle(url)
40
+ currentTheme.value = next
41
+ } catch (error) {
42
+ console.error('Failed to apply theme. Falling back to atom-one-dark:', error)
43
+ removeExistingThemeLinks()
44
+ currentTheme.value = 'atom-one-dark'
45
+ const url = getThemeCssUrl(currentTheme.value)
46
+ await appendStyle(url)
47
+ }
48
+ }
14
49
 
15
50
  const loadHighlight = async () => {
16
51
  if (loaded.value) return
@@ -18,7 +53,7 @@ export function useHighlight() {
18
53
  try {
19
54
  // Load highlight.js
20
55
  await appendScript('https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.0/highlight.min.js', { id: 'hljs-cdn' })
21
- await appendStyle('https://cdn.jsdelivr.net/npm/highlight.js/styles/atom-one-dark.min.css')
56
+ await setTheme(currentTheme.value)
22
57
 
23
58
  if (window.hljs) {
24
59
  hljs.value = window.hljs
@@ -70,6 +105,8 @@ export function useHighlight() {
70
105
  hljs,
71
106
  loaded,
72
107
  loadHighlight,
108
+ setTheme,
109
+ currentTheme,
73
110
  escapeHtml,
74
111
  highlightCode
75
112
  }
@@ -118,16 +118,21 @@ export function useSchemaField<T extends { [key: string]: any }, SP extends Path
118
118
  const condition = field.vIf ?? field['v-if']
119
119
  if (condition !== undefined) {
120
120
  if (typeof condition === 'function') {
121
- if (
122
- !condition(
123
- field.id ? rowData?.[field.id] : undefined,
124
- rowData
125
- )
126
- ) {
121
+ // Compute currentValue for the vIf check
122
+ const vIfCurrentValue = field.id
123
+ ? ('get' in (rowData || {})
124
+ ? (rowData as any)?.get(field.id)
125
+ : (rowData as any)?.[field.id as keyof T])
126
+ : undefined
127
+
128
+ const vIfResult = field.id
129
+ ? condition(vIfCurrentValue, rowData)
130
+ : (condition as any)(rowData)
131
+ if (!vIfResult) {
127
132
  return
128
133
  }
129
134
  } else if (typeof condition === 'string') {
130
- if (!rowData?.[condition]) {
135
+ if (!(rowData as any)?.[condition]) {
131
136
  return
132
137
  }
133
138
  } else if (!condition) {
@@ -136,10 +141,10 @@ export function useSchemaField<T extends { [key: string]: any }, SP extends Path
136
141
  }
137
142
 
138
143
  const {
139
- $el,
144
+ $el: _$el,
140
145
  children,
141
- options,
142
- attrs,
146
+ options: _options,
147
+ attrs: _attrs,
143
148
  class: fieldClass,
144
149
  id,
145
150
  transform,
@@ -156,11 +161,16 @@ export function useSchemaField<T extends { [key: string]: any }, SP extends Path
156
161
  // Use the get method if available, otherwise fall back to direct access
157
162
  const currentValue = field.id
158
163
  ? ('get' in (rowData || {})
159
- ? rowData?.get(field.id)
160
- : rowData?.[field.id])
164
+ ? (rowData as any)?.get(field.id)
165
+ : (rowData as any)?.[field.id as keyof T])
161
166
  : undefined
162
167
 
163
- const transformedValue = transform ? transform(currentValue, rowData) : currentValue
168
+ // Apply transform with conditional args: provide (val,row) only when id exists; otherwise provide only (row)
169
+ const transformedValue = transform
170
+ ? (id
171
+ ? transform(currentValue, rowData)
172
+ : (transform as any)(rowData))
173
+ : currentValue
164
174
 
165
175
  // First bind any function attributes with the current value and row data
166
176
  const boundFieldProps = bindAttrs(fieldProps as Attributes<T, Path<T>> | undefined, currentValue, rowData)
@@ -182,6 +192,18 @@ export function useSchemaField<T extends { [key: string]: any }, SP extends Path
182
192
  onUpdate,
183
193
  }
184
194
 
195
+ // Wire top-level onClick with conditional args
196
+ if (typeof (field as any).onClick === 'function') {
197
+ const original = (field as any).onClick as (val?: any, row?: T) => void
198
+ props.onClick = (..._args: any[]) => {
199
+ if (id) {
200
+ original(currentValue, rowData)
201
+ } else {
202
+ ;(original as any)(rowData)
203
+ }
204
+ }
205
+ }
206
+
185
207
  // For form mode, always use the original value for modelValue
186
208
  if (mode === 'form') {
187
209
  props.modelValue = currentValue
@@ -214,7 +236,16 @@ export function useSchemaField<T extends { [key: string]: any }, SP extends Path
214
236
  const boundAttrs = bindAttrs(field.attrs, currentValue, rowData)
215
237
  Object.entries(boundAttrs).forEach(([key, value]) => {
216
238
  if (typeof value === 'function') {
217
- if (key.startsWith('on')) {
239
+ if (key === 'onClick') {
240
+ const original = value as (val?: any, row?: T) => void
241
+ props.onClick = (..._args: any[]) => {
242
+ if (id) {
243
+ original(currentValue, rowData)
244
+ } else {
245
+ ;(original as any)(rowData)
246
+ }
247
+ }
248
+ } else if (key.startsWith('on')) {
218
249
  props[key] = value
219
250
  } else {
220
251
  props[key] = value(currentValue, rowData)
@@ -1,134 +1,134 @@
1
1
  .bg-dark {
2
- position: fixed;
3
- top: 0;
4
- right: 0;
5
- left: 0;
6
- bottom: 0;
7
- background-color: var(--bgl-dark-bg);
8
- pointer-events: none;
9
- opacity: 0;
10
- transition: all ease-in-out 0.3s;
11
- max-height: 100vh;
12
- overflow: scroll;
13
- margin: 0 auto;
14
- width: 100%;
15
- display: grid;
16
- align-items: center;
17
- overflow-x: hidden;
2
+ position: fixed;
3
+ top: 0;
4
+ right: 0;
5
+ left: 0;
6
+ bottom: 0;
7
+ background-color: var(--bgl-dark-bg);
8
+ pointer-events: none;
9
+ opacity: 0;
10
+ transition: all ease-in-out 0.3s;
11
+ max-height: 100vh;
12
+ overflow: scroll;
13
+ margin: 0 auto;
14
+ width: 100%;
15
+ display: grid;
16
+ align-items: center;
17
+ overflow-x: hidden;
18
18
  }
19
19
 
20
20
  .bg-lignt {
21
- background-color: var(--bgl-white);
21
+ background-color: var(--bgl-white);
22
22
  }
23
23
 
24
24
  .modal {
25
- width: 96%;
26
- max-width: 720px;
27
- /* transform: scale(0.5); */
28
- /* opacity: 0; */
29
- transition: all ease-in-out 0.18s;
30
- margin-left: auto;
31
- margin-right: auto;
32
- height: fit-content;
25
+ width: 96%;
26
+ max-width: 720px;
27
+ /* transform: scale(0.5); */
28
+ /* opacity: 0; */
29
+ transition: all ease-in-out 0.18s;
30
+ margin-left: auto;
31
+ margin-right: auto;
32
+ height: fit-content;
33
33
  }
34
34
 
35
35
  .is-active .modal {
36
- animation: 200ms ease bgl-modal-animation;
36
+ animation: 200ms ease bgl-modal-animation;
37
37
  }
38
38
 
39
39
  @keyframes bgl-modal-animation {
40
- from {
41
- scale: 0.7;
42
- transform: translateY(2rem);
43
- }
40
+ from {
41
+ scale: 0.7;
42
+ transform: translateY(2rem);
43
+ }
44
44
 
45
- to {
46
- scale: 1;
47
- transform: translateY(0);
48
- }
45
+ to {
46
+ scale: 1;
47
+ transform: translateY(0);
48
+ }
49
49
  }
50
50
 
51
51
  .small-modal .modal {
52
- max-width: 300px;
53
- text-align: center;
52
+ max-width: 300px;
53
+ text-align: center;
54
54
  }
55
55
 
56
56
  .tool-bar {
57
- margin: -1rem -1rem 1rem;
58
- display: flex;
59
- justify-content: space-between;
60
- position: -webkit-sticky;
61
- position: sticky;
62
- padding-top: 0rem;
63
- top: 0rem;
64
- z-index: 3;
65
- background: var(--bgl-popup-bg);
66
- border-radius: var(--card-border-radius);
57
+ margin: -1rem -1rem 1rem;
58
+ display: flex;
59
+ justify-content: space-between;
60
+ position: -webkit-sticky;
61
+ position: sticky;
62
+ padding-top: 0rem;
63
+ top: 0rem;
64
+ z-index: 3;
65
+ background: var(--bgl-popup-bg);
66
+ /* border-radius: var(--card-border-radius); */
67
67
  }
68
68
 
69
69
  .modal-size {
70
- cursor: pointer;
70
+ cursor: pointer;
71
71
  }
72
72
 
73
73
  .is-side .modal {
74
- inset-inline-end: -1720px;
75
- opacity: 1;
76
- max-width: 600px;
77
- width: 90%;
78
- margin-top: 1rem;
79
- margin-bottom: 1rem;
80
- margin-inline-start: auto;
81
- margin-inline-end: 20px;
82
- min-height: calc(100vh - 40px);
83
- transform: translateX(100%);
74
+ inset-inline-end: -1720px;
75
+ opacity: 1;
76
+ max-width: 600px;
77
+ width: 90%;
78
+ margin-top: 1rem;
79
+ margin-bottom: 1rem;
80
+ margin-inline-start: auto;
81
+ margin-inline-end: 20px;
82
+ min-height: calc(100vh - 40px);
83
+ transform: translateX(100%);
84
84
  }
85
85
 
86
86
  .is-active .modal {
87
- opacity: 1;
88
- box-shadow: 6px 6px 20px 20px #0000001c;
87
+ opacity: 1;
88
+ box-shadow: 6px 6px 20px 20px #0000001c;
89
89
  }
90
90
 
91
91
  .bg-lignt .modal {
92
- border: 1px solid var(--border-color);
92
+ border: 1px solid var(--border-color);
93
93
  }
94
94
 
95
95
  .bg-lignt.is-active .modal {
96
- box-shadow: none;
96
+ box-shadow: none;
97
97
  }
98
98
 
99
99
  .is-active.is-side .modal {
100
- inset-inline-end: 0px;
101
- transform: translateX(0%);
100
+ inset-inline-end: 0px;
101
+ transform: translateX(0%);
102
102
  }
103
103
 
104
104
  .bg-dark.is-active {
105
- opacity: 1;
106
- pointer-events: all;
105
+ opacity: 1;
106
+ pointer-events: all;
107
107
  }
108
108
 
109
109
  body:has(.bg-dark.is-active) {
110
- overflow: hidden;
110
+ overflow: hidden;
111
111
  }
112
112
 
113
113
  .is-side.bg-dark.is-active {
114
- opacity: 1;
115
- align-items: stretch;
114
+ opacity: 1;
115
+ align-items: stretch;
116
116
  }
117
117
 
118
118
  .is-side.is-active .modal {
119
- pointer-events: all;
119
+ pointer-events: all;
120
120
  }
121
121
 
122
122
  @media screen and (max-width: 910px) {
123
- .tool-bar {
124
- margin: -1rem 0rem 1rem;
125
- padding-bottom: 1rem;
126
- align-items: center;
127
- }
128
-
129
- .is-active.is-side .modal {
130
- margin-inline-end: 2%;
131
- margin-inline-start: 2%;
132
- width: 98%;
133
- }
134
- }
123
+ .tool-bar {
124
+ margin: -1rem 0rem 1rem;
125
+ padding-bottom: 1rem;
126
+ align-items: center;
127
+ }
128
+
129
+ .is-active.is-side .modal {
130
+ margin-inline-end: 2%;
131
+ margin-inline-start: 2%;
132
+ width: 98%;
133
+ }
134
+ }
@@ -77,13 +77,13 @@ export interface ElementField<
77
77
  T,
78
78
  PO extends PathsOptions = DefaultPathsOptions,
79
79
  > {
80
- $el: string
80
+ $el: any
81
81
  id?: Path<T, PO> | string
82
82
  class?: any
83
83
  vIf?: any
84
84
  style?: Record<string, any>
85
85
  attrs?: Record<string, any>
86
- onClick?: () => void
86
+ onClick?: (val?: any, rowData?: T) => void
87
87
  transform?: (val?: any, rowData?: T) => any
88
88
  children?: ElementField<T, PO>[]
89
89
  }
@@ -8,6 +8,7 @@ export interface InputOptions<
8
8
  > extends Partial<BaseBagelField<T, P>> {
9
9
  defaultValue?: string | number
10
10
  autocomplete?: AutoFillField
11
+ helpText?: string
11
12
  }
12
13
 
13
14
  export interface DateOptions<T, K extends Path<T>> extends InputOptions<T, K> {
@@ -1,4 +1,6 @@
1
- import type { IconType, Option, Path, ElementField } from '@bagelink/vue'
1
+ import type { ElementField, IconType, Option, Path } from '@bagelink/vue'
2
+ import { Dropdown, ListItem } from '@bagelink/vue'
3
+ import { markRaw } from 'vue'
2
4
 
3
5
  export type DefaultPathsOptions = Record<string, any>
4
6
  export type PathsOptions = Record<string, any>
@@ -30,7 +32,6 @@ export interface TxtElementOptions<
30
32
  weight?: 'light' | 'normal' | 'medium' | 'semibold' | 'bold'
31
33
  color?: string
32
34
  align?: 'left' | 'center' | 'right' | 'justify'
33
- transform?: (val?: any, rowData?: T) => any
34
35
  }
35
36
 
36
37
  export interface ImgElementOptions<
@@ -64,6 +65,7 @@ export interface ListItemElementOptions<
64
65
  T = any,
65
66
  PO extends PathsOptions = DefaultPathsOptions
66
67
  > extends Partial<BaseElementField<T, PO>> {
68
+ id?: Path<T, PO>
67
69
  title?: string
68
70
  subtitle?: string
69
71
  icon?: IconType
@@ -373,8 +375,8 @@ export function img<
373
375
  class: finalOptions.class,
374
376
  vIf: finalOptions.vIf,
375
377
  style: finalOptions.style,
378
+ transform: finalOptions.transform,
376
379
  attrs: {
377
- src: finalSrc || (finalOptions.src ?? '') || '',
378
380
  alt: finalOptions.alt,
379
381
  width: finalOptions.width,
380
382
  height: finalOptions.height,
@@ -386,50 +388,68 @@ export function img<
386
388
  }
387
389
  }
388
390
 
389
- // Dropdown element function following BagelForm pattern
390
- export function dropdownElement<
391
+ export function dropdown<
391
392
  T = any,
392
393
  PO extends PathsOptions = DefaultPathsOptions
393
394
  >(
394
- id?: Path<T, PO>,
395
- options?: Option[] | (() => Option[]),
396
- config?: DropdownElementOptions<T, PO>,
395
+ ...args: Array<DropdownElementOptions<T, PO> | BaseElementField<T, PO>>
397
396
  ): BaseElementField<T, PO> {
397
+ let opts: DropdownElementOptions<T, PO> | undefined
398
+ let children: BaseElementField<T, PO>[] = []
399
+
400
+ if (args.length > 0) {
401
+ const firstArg = args[0] as DropdownElementOptions<T, PO> | BaseElementField<T, PO>
402
+ if (
403
+ typeof firstArg === 'object'
404
+ && firstArg !== null
405
+ && ('$el' in (firstArg as Record<string, unknown>))
406
+ ) {
407
+ children = args as BaseElementField<T, PO>[]
408
+ } else {
409
+ opts = firstArg as DropdownElementOptions<T, PO>
410
+ children = args.slice(1) as BaseElementField<T, PO>[]
411
+ }
412
+ }
413
+
414
+ const attrs: Record<string, unknown> = {}
415
+ if (opts?.attrs) Object.assign(attrs, opts.attrs)
416
+ if (opts) {
417
+ if (opts.options != null) attrs.options = opts.options
418
+ if (opts.placeholder != null) attrs.placeholder = opts.placeholder
419
+ if (opts.searchable != null) attrs.searchable = opts.searchable
420
+ if (opts.multiselect != null) attrs.multiselect = opts.multiselect
421
+ if (opts.clearable != null) attrs.clearable = opts.clearable
422
+ if (opts.onSelect != null) attrs.onSelect = opts.onSelect
423
+ if (opts.onSearch != null) attrs.onSearch = opts.onSearch
424
+ }
425
+
398
426
  return {
399
- $el: 'dropdown',
400
- id,
401
- class: config?.class,
402
- vIf: config?.vIf,
403
- style: config?.style,
404
- attrs: {
405
- options: options || config?.options,
406
- placeholder: config?.placeholder,
407
- searchable: config?.searchable,
408
- multiselect: config?.multiselect,
409
- clearable: config?.clearable,
410
- onSelect: config?.onSelect,
411
- onSearch: config?.onSearch,
412
- },
427
+ $el: markRaw(Dropdown),
428
+ class: opts?.class,
429
+ vIf: opts?.vIf,
430
+ style: opts?.style,
431
+ attrs,
432
+ children,
413
433
  }
414
434
  }
415
435
 
416
436
  // ListItem element function following BagelForm pattern
417
- export function listItemElement<
437
+ export function listItem<
418
438
  T = any,
419
439
  PO extends PathsOptions = DefaultPathsOptions
420
440
  >(
421
- id?: Path<T, PO>,
422
441
  title?: string,
423
442
  options?: ListItemElementOptions<T, PO>,
424
443
  ): BaseElementField<T, PO> {
425
444
  return {
426
- $el: 'listItem',
427
- id,
445
+ $el: markRaw(ListItem),
446
+ id: options?.id,
428
447
  class: options?.class,
429
448
  vIf: options?.vIf,
430
449
  style: options?.style,
431
450
  onClick: options?.onClick,
432
451
  attrs: {
452
+ thin: true,
433
453
  title: title ?? options?.title ?? '',
434
454
  subtitle: options?.subtitle,
435
455
  icon: options?.icon,
@@ -443,7 +463,7 @@ export function listItemElement<
443
463
  }
444
464
 
445
465
  // Container element function following BagelForm pattern
446
- export function containerElement<
466
+ export function container<
447
467
  T = any,
448
468
  PO extends PathsOptions = DefaultPathsOptions
449
469
  >(
@@ -494,6 +514,7 @@ export function column<T = any, P extends Path<T, PO> = any, PO extends PathsOpt
494
514
  class: options?.class ?? 'column-class',
495
515
  vIf: options?.vIf,
496
516
  style: options?.style,
517
+ transform: options?.transform,
497
518
  attrs: {
498
519
  ...options?.attrs,
499
520
  label: label ?? options?.attrs?.label ?? '',
@@ -510,9 +531,9 @@ export function useElements() {
510
531
  iconBtn,
511
532
  txt,
512
533
  img,
513
- dropdownElement,
514
- listItemElement,
515
- containerElement,
534
+ dropdown,
535
+ listItem,
536
+ container,
516
537
  findElementById,
517
538
  getBaseElementField,
518
539
  col,