@bagelink/vue 0.0.1260 → 0.0.1268
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.
- package/dist/components/AddressSearch.vue.d.ts +6 -0
- package/dist/components/AddressSearch.vue.d.ts.map +1 -1
- package/dist/components/DataTable/DataTable.vue.d.ts.map +1 -1
- package/dist/components/DropDown.vue.d.ts +51 -48
- package/dist/components/DropDown.vue.d.ts.map +1 -1
- package/dist/components/form/BagelForm.vue.d.ts.map +1 -1
- package/dist/components/form/FieldArray.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/DateInput.vue.d.ts +4 -1
- package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/PasswordInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RadioGroup.vue.d.ts +1 -1
- package/dist/components/form/inputs/RichText/composables/useCommands.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/composables/useEditor.d.ts +31 -23
- package/dist/components/form/inputs/RichText/composables/useEditor.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts +2 -1
- package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/config.d.ts +2 -1
- package/dist/components/form/inputs/RichText/config.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/commands.d.ts +1 -0
- package/dist/components/form/inputs/RichText/utils/commands.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/media.d.ts +5 -3
- package/dist/components/form/inputs/RichText/utils/media.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/selection.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts +12 -0
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/TelInput.vue.d.ts +9 -3
- package/dist/components/form/inputs/TelInput.vue.d.ts.map +1 -1
- package/dist/components/form/useBagelFormState.d.ts.map +1 -1
- package/dist/editor-7QC0nG_c.js +4 -0
- package/dist/editor-CpMNx6Eo.cjs +4 -0
- package/dist/index.cjs +1731 -1191
- package/dist/index.mjs +1732 -1192
- package/dist/style.css +90 -83
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/DataTable/DataTable.vue +7 -1
- package/src/components/Dropdown.vue +5 -2
- package/src/components/form/BagelForm.vue +2 -13
- package/src/components/form/FieldArray.vue +3 -0
- package/src/components/form/inputs/DateInput.vue +341 -162
- package/src/components/form/inputs/PasswordInput.vue +5 -1
- package/src/components/form/inputs/RichText/components/EditorToolbar.vue +2 -2
- package/src/components/form/inputs/RichText/composables/useCommands.ts +53 -97
- package/src/components/form/inputs/RichText/composables/useEditor.ts +377 -270
- package/src/components/form/inputs/RichText/composables/useEditorKeyboard.ts +124 -58
- package/src/components/form/inputs/RichText/config.ts +27 -3
- package/src/components/form/inputs/RichText/editor.css +29 -0
- package/src/components/form/inputs/RichText/index.vue +129 -55
- package/src/components/form/inputs/RichText/richTextTypes.d.ts +35 -49
- package/src/components/form/inputs/RichText/utils/commands.ts +181 -0
- package/src/components/form/inputs/RichText/utils/media.ts +64 -3
- package/src/components/form/inputs/RichText/utils/selection.ts +40 -5
- package/src/components/form/useBagelFormState.ts +2 -14
- package/src/utils/index.ts +15 -0
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { EditorState } from '../richTextTypes'
|
|
2
|
+
import { formatting } from './formatting'
|
|
3
|
+
import { insertImage, insertLink, insertEmbed } from './media'
|
|
4
|
+
import { addRow, deleteRow, mergeCells, splitCell, insertTable, deleteTable, insertColumn, deleteColumn, alignColumn } from './table'
|
|
2
5
|
|
|
3
6
|
export interface Command {
|
|
4
7
|
name: string
|
|
@@ -17,6 +20,184 @@ export interface CommandExecutor {
|
|
|
17
20
|
getValue: (command: string) => string | null
|
|
18
21
|
}
|
|
19
22
|
|
|
23
|
+
// Centralized command creation helper
|
|
24
|
+
function createCommand(name: string, execute: Command['execute'], isActive?: Command['isActive']): Command {
|
|
25
|
+
return {
|
|
26
|
+
name,
|
|
27
|
+
execute: (state: EditorState, value?: string) => {
|
|
28
|
+
if (!state.doc) return
|
|
29
|
+
execute(state, value)
|
|
30
|
+
},
|
|
31
|
+
isActive
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Create formatting commands helper
|
|
36
|
+
function createFormattingCommand(state: EditorState, type: 'text' | 'block' | 'list', command: string, tag?: string): Command {
|
|
37
|
+
const format = formatting(state)
|
|
38
|
+
return createCommand(
|
|
39
|
+
command,
|
|
40
|
+
() => {
|
|
41
|
+
if (!state.doc) return
|
|
42
|
+
|
|
43
|
+
if (type === 'text') {
|
|
44
|
+
if (command === 'bold') state.doc.execCommand('bold', false)
|
|
45
|
+
else if (command === 'italic') state.doc.execCommand('italic', false)
|
|
46
|
+
else if (command === 'underline') state.doc.execCommand('underline', false)
|
|
47
|
+
else format.text(command)
|
|
48
|
+
}
|
|
49
|
+
else if (type === 'block') {
|
|
50
|
+
state.doc.execCommand('formatBlock', false, `<${tag || command}>`)
|
|
51
|
+
}
|
|
52
|
+
else if (type === 'list') {
|
|
53
|
+
const selection = state.doc.getSelection()
|
|
54
|
+
if (!selection || !selection.rangeCount) return
|
|
55
|
+
|
|
56
|
+
const range = selection.getRangeAt(0)
|
|
57
|
+
|
|
58
|
+
// If there's no content or the selection is collapsed
|
|
59
|
+
if (range.collapsed && (!range.startContainer.textContent?.trim() || range.startContainer === state.doc.body)) {
|
|
60
|
+
// Create a new list with an empty item
|
|
61
|
+
const list = state.doc.createElement(command === 'orderedList' ? 'ol' : 'ul')
|
|
62
|
+
const li = state.doc.createElement('li')
|
|
63
|
+
// Use a non-breaking space with br to ensure proper rendering
|
|
64
|
+
li.innerHTML = ' <br>'
|
|
65
|
+
list.appendChild(li)
|
|
66
|
+
|
|
67
|
+
// If we're in an empty paragraph, replace it
|
|
68
|
+
const currentBlock = range.startContainer.nodeType === 1
|
|
69
|
+
? range.startContainer as Element
|
|
70
|
+
: range.startContainer.parentElement
|
|
71
|
+
|
|
72
|
+
if (currentBlock?.tagName.toLowerCase() === 'p' && isNodeEmpty(currentBlock)) {
|
|
73
|
+
currentBlock.parentNode?.replaceChild(list, currentBlock)
|
|
74
|
+
} else {
|
|
75
|
+
// Otherwise insert at cursor
|
|
76
|
+
range.insertNode(list)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Move cursor into the list item
|
|
80
|
+
range.selectNodeContents(li)
|
|
81
|
+
range.collapse(true)
|
|
82
|
+
selection.removeAllRanges()
|
|
83
|
+
selection.addRange(range)
|
|
84
|
+
} else {
|
|
85
|
+
// Use standard command for existing content
|
|
86
|
+
state.doc.execCommand(
|
|
87
|
+
command === 'orderedList' ? 'insertOrderedList' : 'insertUnorderedList',
|
|
88
|
+
false
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
() => state.selectedStyles.has(command)
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Helper function to check if a node is empty (contains only whitespace or <br>)
|
|
98
|
+
function isNodeEmpty(node: Node): boolean {
|
|
99
|
+
const text = node.textContent?.trim() || ''
|
|
100
|
+
if (text) return false
|
|
101
|
+
|
|
102
|
+
// Check for <br> tags
|
|
103
|
+
const brElements = (node as Element).getElementsByTagName('br')
|
|
104
|
+
if (brElements.length === 0) return true
|
|
105
|
+
|
|
106
|
+
// If there's only one <br> and it's the only content, consider it empty
|
|
107
|
+
return brElements.length === 1 && node.childNodes.length === 1
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function createCommandRegistry(state: EditorState): CommandRegistry {
|
|
111
|
+
const format = formatting(state)
|
|
112
|
+
|
|
113
|
+
// History commands
|
|
114
|
+
const historyCommands = {
|
|
115
|
+
undo: createCommand('Undo', () => state.doc?.execCommand('undo', false)),
|
|
116
|
+
redo: createCommand('Redo', () => state.doc?.execCommand('redo', false))
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Basic text formatting commands
|
|
120
|
+
const textCommands = ['bold', 'italic', 'underline'].reduce((acc, cmd) => ({
|
|
121
|
+
...acc,
|
|
122
|
+
[cmd]: createFormattingCommand(state, 'text', cmd)
|
|
123
|
+
}), {})
|
|
124
|
+
|
|
125
|
+
// Heading commands
|
|
126
|
+
const headingCommands = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].reduce((acc, cmd) => ({
|
|
127
|
+
...acc,
|
|
128
|
+
[cmd]: createFormattingCommand(state, 'block', cmd)
|
|
129
|
+
}), {})
|
|
130
|
+
|
|
131
|
+
// Block commands
|
|
132
|
+
const blockCommands = {
|
|
133
|
+
p: createFormattingCommand(state, 'block', 'p'),
|
|
134
|
+
blockquote: createFormattingCommand(state, 'block', 'blockquote')
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// List commands
|
|
138
|
+
const listCommands = {
|
|
139
|
+
orderedList: createFormattingCommand(state, 'list', 'orderedList'),
|
|
140
|
+
unorderedList: createFormattingCommand(state, 'list', 'unorderedList')
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Table commands
|
|
144
|
+
const tableCommands = {
|
|
145
|
+
insertTable: createCommand('Insert Table', (state, value) => {
|
|
146
|
+
const [rows, cols] = value?.split('x').map(Number) || [3, 3]
|
|
147
|
+
insertTable(rows, cols, state)
|
|
148
|
+
}),
|
|
149
|
+
deleteTable: createCommand('Delete Table', state => state.range && deleteTable(state.range)),
|
|
150
|
+
mergeCells: createCommand('Merge Cells', state => state.range && state.doc && mergeCells(state.range, state.doc)),
|
|
151
|
+
splitCells: createCommand('Split Cells', state => state.range && state.doc && splitCell(state.range, state.doc)),
|
|
152
|
+
addRowBefore: createCommand('Add Row Before', state => state.range && state.doc && addRow('before', state.range, state.doc)),
|
|
153
|
+
addRowAfter: createCommand('Add Row After', state => state.range && state.doc && addRow('after', state.range, state.doc)),
|
|
154
|
+
deleteRow: createCommand('Delete Row', state => state.range && deleteRow(state.range)),
|
|
155
|
+
insertColumnLeft: createCommand('Insert Column Left', state => state.range && insertColumn('before', state.range)),
|
|
156
|
+
insertColumnRight: createCommand('Insert Column Right', state => state.range && insertColumn('after', state.range)),
|
|
157
|
+
deleteColumn: createCommand('Delete Column', state => state.range && deleteColumn(state.range))
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Alignment commands
|
|
161
|
+
const alignmentCommands = ['Left', 'Center', 'Right', 'Justify'].reduce((acc, align) => ({
|
|
162
|
+
...acc,
|
|
163
|
+
[`align${align}`]: createCommand(`Align ${align}`, state => state.range && alignColumn(state.range, align.toLowerCase() as 'left' | 'center' | 'right' | 'justify'))
|
|
164
|
+
}), {})
|
|
165
|
+
|
|
166
|
+
// View state commands
|
|
167
|
+
const viewCommands = {
|
|
168
|
+
fullScreen: createCommand('Full Screen', (state) => { state.isFullscreen = !state.isFullscreen }, state => state.isFullscreen),
|
|
169
|
+
splitView: createCommand('Split View', (state) => { state.isSplitView = !state.isSplitView }, state => state.isSplitView),
|
|
170
|
+
codeView: createCommand('Code View', (state) => { state.isCodeView = !state.isCodeView }, state => state.isCodeView)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Media commands
|
|
174
|
+
const mediaCommands = {
|
|
175
|
+
image: createCommand('Insert Image', state => state.modal && insertImage(state.modal, state)),
|
|
176
|
+
link: createCommand('Insert Link', state => state.modal && state.range && insertLink(state.modal, state)),
|
|
177
|
+
embed: createCommand('Insert Embed', state => state.modal && insertEmbed(state.modal, state))
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Other formatting commands
|
|
181
|
+
const otherCommands = {
|
|
182
|
+
clear: createCommand('Clear Formatting', () => { format.clear() }),
|
|
183
|
+
indent: createCommand('Indent', () => { format.text('indent') }),
|
|
184
|
+
outdent: createCommand('Outdent', () => { format.text('outdent') })
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
...historyCommands,
|
|
189
|
+
...textCommands,
|
|
190
|
+
...headingCommands,
|
|
191
|
+
...blockCommands,
|
|
192
|
+
...listCommands,
|
|
193
|
+
...tableCommands,
|
|
194
|
+
...alignmentCommands,
|
|
195
|
+
...viewCommands,
|
|
196
|
+
...mediaCommands,
|
|
197
|
+
...otherCommands
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
20
201
|
export function createCommandExecutor(state: EditorState, commands: CommandRegistry): CommandExecutor {
|
|
21
202
|
return {
|
|
22
203
|
execute(command: string, value?: string) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Modal } from '@bagelink/vue'
|
|
2
|
+
import type { EditorState } from '../richTextTypes'
|
|
2
3
|
import { bagelFormUtils } from '@bagelink/vue'
|
|
3
4
|
|
|
4
5
|
const { frmRow, numField } = bagelFormUtils
|
|
5
6
|
|
|
6
|
-
export function insertImage(modal: Modal, state: EditorState) {
|
|
7
|
+
export function insertImage(modal: typeof Modal, state: EditorState) {
|
|
7
8
|
const { range, doc } = state
|
|
8
9
|
if (!range || !doc) return
|
|
9
10
|
|
|
@@ -45,7 +46,7 @@ export function insertImage(modal: Modal, state: EditorState) {
|
|
|
45
46
|
})
|
|
46
47
|
}
|
|
47
48
|
|
|
48
|
-
export function insertLink(modal: Modal, state: EditorState) {
|
|
49
|
+
export function insertLink(modal: typeof Modal, state: EditorState) {
|
|
49
50
|
const { range, doc } = state
|
|
50
51
|
if (!range || !doc) return
|
|
51
52
|
|
|
@@ -65,3 +66,63 @@ export function insertLink(modal: Modal, state: EditorState) {
|
|
|
65
66
|
}
|
|
66
67
|
})
|
|
67
68
|
}
|
|
69
|
+
|
|
70
|
+
export function insertEmbed(modal: typeof Modal, state: EditorState) {
|
|
71
|
+
const { range, doc } = state
|
|
72
|
+
if (!range || !doc) return
|
|
73
|
+
|
|
74
|
+
modal.showModalForm({
|
|
75
|
+
title: 'Insert Embed',
|
|
76
|
+
schema: [
|
|
77
|
+
{ id: 'url', $el: 'text', label: 'URL', attrs: { placeholder: 'Enter URL (YouTube, Vimeo, etc.)' } },
|
|
78
|
+
frmRow(
|
|
79
|
+
numField('width', 'Width', { min: 200, placeholder: '560' }),
|
|
80
|
+
numField('height', 'Height', { min: 200, placeholder: '315' })
|
|
81
|
+
),
|
|
82
|
+
{ id: 'allowFullscreen', $el: 'check', label: 'Allow Fullscreen', value: true },
|
|
83
|
+
],
|
|
84
|
+
onSubmit: (data: { url: string, width?: number, height?: number, allowFullscreen?: boolean }) => {
|
|
85
|
+
if (!data.url) return
|
|
86
|
+
|
|
87
|
+
// Convert common video URLs to embed URLs
|
|
88
|
+
const url = new URL(data.url)
|
|
89
|
+
let embedUrl = data.url
|
|
90
|
+
|
|
91
|
+
// YouTube
|
|
92
|
+
if (url.hostname.includes('youtube.com') || url.hostname === 'youtu.be') {
|
|
93
|
+
const videoId = url.hostname === 'youtu.be'
|
|
94
|
+
? url.pathname.slice(1)
|
|
95
|
+
: url.searchParams.get('v')
|
|
96
|
+
if (videoId) {
|
|
97
|
+
embedUrl = `https://www.youtube.com/embed/${videoId}`
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Vimeo
|
|
101
|
+
else if (url.hostname.includes('vimeo.com')) {
|
|
102
|
+
const videoId = url.pathname.split('/').pop()
|
|
103
|
+
if (videoId) {
|
|
104
|
+
embedUrl = `https://player.vimeo.com/video/${videoId}`
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const iframe = doc.createElement('iframe')
|
|
109
|
+
Object.assign(iframe, {
|
|
110
|
+
src: embedUrl,
|
|
111
|
+
width: data.width || 560,
|
|
112
|
+
height: data.height || 315,
|
|
113
|
+
frameBorder: '0',
|
|
114
|
+
allow: 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture',
|
|
115
|
+
allowFullscreen: data.allowFullscreen
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// Create a wrapper div for proper alignment and spacing
|
|
119
|
+
const wrapper = doc.createElement('div')
|
|
120
|
+
wrapper.style.textAlign = 'center'
|
|
121
|
+
wrapper.style.margin = '1em 0'
|
|
122
|
+
wrapper.appendChild(iframe)
|
|
123
|
+
|
|
124
|
+
range.deleteContents()
|
|
125
|
+
range.insertNode(wrapper)
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
}
|
|
@@ -12,23 +12,58 @@ export function isStyleActive(style: string, doc: Document) {
|
|
|
12
12
|
|
|
13
13
|
const range = selection.getRangeAt(0)
|
|
14
14
|
const container = range.commonAncestorContainer
|
|
15
|
-
const parent = container.nodeType ===
|
|
15
|
+
const parent = container.nodeType === Node.TEXT_NODE ? container.parentElement : container as Element
|
|
16
16
|
|
|
17
17
|
if (!parent) return false
|
|
18
18
|
|
|
19
19
|
const checkParent = (element: Element | null, tags: string[]): boolean => {
|
|
20
|
-
if (!element) return false
|
|
21
|
-
|
|
20
|
+
if (!element || !element.tagName) return false
|
|
21
|
+
const tagName = element.tagName.toLowerCase()
|
|
22
|
+
if (tags.includes(tagName)) return true
|
|
22
23
|
return checkParent(element.parentElement, tags)
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
// Define style mappings for different types of formatting
|
|
27
|
+
const styleMappings: { [key: string]: string[] } = {
|
|
28
|
+
// Inline styles
|
|
26
29
|
bold: ['strong', 'b'],
|
|
27
30
|
italic: ['em', 'i'],
|
|
28
31
|
underline: ['u'],
|
|
32
|
+
|
|
33
|
+
// Block styles
|
|
34
|
+
h1: ['h1'],
|
|
35
|
+
h2: ['h2'],
|
|
36
|
+
h3: ['h3'],
|
|
37
|
+
h4: ['h4'],
|
|
38
|
+
h5: ['h5'],
|
|
39
|
+
h6: ['h6'],
|
|
40
|
+
p: ['p'],
|
|
41
|
+
blockquote: ['blockquote'],
|
|
42
|
+
|
|
43
|
+
// List styles
|
|
44
|
+
orderedList: ['ol'],
|
|
45
|
+
unorderedList: ['ul']
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Special handling for view state commands
|
|
49
|
+
if (['splitView', 'codeView', 'fullScreen'].includes(style)) {
|
|
50
|
+
return false // These are handled by the editor state directly
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Special handling for list items
|
|
54
|
+
if (style === 'orderedList' || style === 'unorderedList') {
|
|
55
|
+
const listParent = parent.closest(style === 'orderedList' ? 'ol' : 'ul')
|
|
56
|
+
return !!listParent
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check for the style in the style mappings
|
|
60
|
+
const tags = styleMappings[style]
|
|
61
|
+
if (tags) {
|
|
62
|
+
return checkParent(parent, tags)
|
|
29
63
|
}
|
|
30
64
|
|
|
31
|
-
|
|
65
|
+
// Default to checking the style itself if no mapping exists
|
|
66
|
+
return checkParent(parent, [style])
|
|
32
67
|
}
|
|
33
68
|
|
|
34
69
|
export interface SelectionInfo {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Ref } from 'vue'
|
|
2
2
|
import { inject, provide, ref } from 'vue'
|
|
3
|
+
import { getNestedValue } from '../../utils'
|
|
3
4
|
|
|
4
5
|
export const FORM_STATE_KEY = Symbol('bagelFormState')
|
|
5
6
|
|
|
@@ -30,20 +31,7 @@ export function provideBagelFormState<T>(initialData: T) {
|
|
|
30
31
|
const data = ref(initialData) as Ref<T>
|
|
31
32
|
const isDirty = ref(false)
|
|
32
33
|
|
|
33
|
-
const getFieldData = (path?: string) =>
|
|
34
|
-
if (!path) return ''
|
|
35
|
-
const keys = path.split(/[.[]/)
|
|
36
|
-
let current = data.value as any
|
|
37
|
-
|
|
38
|
-
for (let i = 0; i < keys.length; i++) {
|
|
39
|
-
const key = keys[i]
|
|
40
|
-
if (!current || typeof current !== 'object' || !(key in current)) {
|
|
41
|
-
return ''
|
|
42
|
-
}
|
|
43
|
-
current = current[key]
|
|
44
|
-
}
|
|
45
|
-
return current ?? ''
|
|
46
|
-
}
|
|
34
|
+
const getFieldData = (path?: string) => getNestedValue(data.value, path, '')
|
|
47
35
|
|
|
48
36
|
const updateField = (path: string, value: any) => {
|
|
49
37
|
const keys = path.split(/[.[]/)
|
package/src/utils/index.ts
CHANGED
|
@@ -180,3 +180,18 @@ export function pathKeyToURL(pathKey?: string) {
|
|
|
180
180
|
}
|
|
181
181
|
return `${fileBaseUrl}/${pathKey}`
|
|
182
182
|
}
|
|
183
|
+
|
|
184
|
+
export function getNestedValue(obj: any, path?: string, defaultValue: any = undefined): any {
|
|
185
|
+
if (!path) return obj
|
|
186
|
+
const keys = path.split(/[.[]/)
|
|
187
|
+
let current = obj
|
|
188
|
+
|
|
189
|
+
for (const key of keys) {
|
|
190
|
+
if (!current || typeof current !== 'object' || !(key in current)) {
|
|
191
|
+
return defaultValue
|
|
192
|
+
}
|
|
193
|
+
current = current[key]
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return current ?? defaultValue
|
|
197
|
+
}
|