@bagelink/vue 1.17.2 → 1.18.3
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/assetTransport.d.ts +9 -31
- package/dist/assetTransport.d.ts.map +1 -1
- package/dist/assets.d.ts +39 -20
- package/dist/assets.d.ts.map +1 -1
- package/dist/components/AddressSearch.vue.d.ts +40 -36
- package/dist/components/AddressSearch.vue.d.ts.map +1 -1
- package/dist/components/ContextMenu.vue.d.ts +518 -0
- package/dist/components/ContextMenu.vue.d.ts.map +1 -0
- package/dist/components/Dropdown.types.d.ts +25 -0
- package/dist/components/Dropdown.types.d.ts.map +1 -0
- package/dist/components/Dropdown.vue.d.ts +20 -15
- package/dist/components/Dropdown.vue.d.ts.map +1 -1
- package/dist/components/FilterQuery.vue.d.ts +1 -1
- package/dist/components/FilterQuery.vue.d.ts.map +1 -1
- package/dist/components/Icon/Icon.vue.d.ts +0 -1
- package/dist/components/Icon/Icon.vue.d.ts.map +1 -1
- package/dist/components/Icon/constants.d.ts +0 -1
- package/dist/components/Icon/constants.d.ts.map +1 -1
- package/dist/components/Icon/types.d.ts +2 -3
- package/dist/components/Icon/types.d.ts.map +1 -1
- package/dist/components/Swiper.vue.d.ts +1 -1
- package/dist/components/draggable/useDraggable.d.ts.map +1 -1
- package/dist/components/form/inputs/IconPickerInput.vue.d.ts +48 -37
- package/dist/components/form/inputs/IconPickerInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/components/EditorToolbar.vue.d.ts.map +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 +3 -0
- package/dist/components/form/inputs/RichText/composables/useEditor.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts +1 -1
- package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.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.map +1 -1
- package/dist/components/form/inputs/RichText/utils/selection.d.ts +2 -0
- package/dist/components/form/inputs/RichText/utils/selection.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/utils/table.d.ts +34 -8
- package/dist/components/form/inputs/RichText/utils/table.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts +74 -65
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/TelInput.vue.d.ts +2 -2
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/composables/index.d.ts +1 -1
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/useBoundedRectDrag.d.ts.map +1 -1
- package/dist/composables/usePointerSession.d.ts +14 -3
- package/dist/composables/usePointerSession.d.ts.map +1 -1
- package/dist/core.css +3 -3
- package/dist/directives/vResize.d.ts.map +1 -1
- package/dist/form-flow/FormFlow.vue.d.ts.map +1 -1
- package/dist/form-flow/form-flow.d.ts +4 -2
- package/dist/form-flow/form-flow.d.ts.map +1 -1
- package/dist/form-flow/schema-fields.d.ts +4 -1
- package/dist/form-flow/schema-fields.d.ts.map +1 -1
- package/dist/index.cjs +240 -153
- package/dist/index.css +1 -1
- package/dist/index.mjs +18461 -18283
- package/dist/style.css +3 -3
- package/dist/types/BagelForm.d.ts +2 -0
- package/dist/types/BagelForm.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/assetTransport.test.ts +33 -83
- package/src/assetTransport.ts +24 -90
- package/src/assets.test.ts +58 -99
- package/src/assets.ts +78 -161
- package/src/components/ContextMenu.vue +113 -0
- package/src/components/Dropdown.types.ts +27 -0
- package/src/components/Dropdown.vue +129 -64
- package/src/components/FilterQuery.vue +1 -1
- package/src/components/Icon/Icon.vue +8 -30
- package/src/components/Icon/constants.ts +0 -166
- package/src/components/Icon/types.ts +2 -3
- package/src/components/draggable/useDraggable.ts +67 -100
- package/src/components/form/inputs/IconPickerInput.vue +14 -9
- package/src/components/form/inputs/RichText/components/EditorToolbar.vue +43 -111
- package/src/components/form/inputs/RichText/composables/useCommands.ts +32 -54
- package/src/components/form/inputs/RichText/composables/useEditor.ts +45 -171
- package/src/components/form/inputs/RichText/composables/useEditorKeyboard.ts +90 -14
- package/src/components/form/inputs/RichText/index.vue +1195 -1210
- package/src/components/form/inputs/RichText/utils/commands.ts +689 -1028
- package/src/components/form/inputs/RichText/utils/selection.ts +66 -41
- package/src/components/form/inputs/RichText/utils/table.ts +798 -136
- package/src/components/form/inputs/SelectInput.vue +6 -4
- package/src/components/form/inputs/Upload/AssetUploadInput.vue +2 -2
- package/src/components/index.ts +3 -1
- package/src/composables/index.ts +1 -1
- package/src/composables/useBoundedRectDrag.ts +9 -12
- package/src/composables/usePointerSession.ts +57 -12
- package/src/directives/vResize.ts +6 -7
- package/src/form-flow/FormFlow.vue +4 -0
- package/src/form-flow/form-flow.ts +8 -3
- package/src/form-flow/schema-fields.ts +9 -4
- package/src/styles/core.css +1 -18
- package/src/types/BagelForm.ts +2 -0
- package/dist/components/form/inputs/RichText/utils/formatting.d.ts +0 -10
- package/dist/components/form/inputs/RichText/utils/formatting.d.ts.map +0 -1
- package/src/components/form/inputs/RichText/components/gridBox.vue +0 -58
- package/src/components/form/inputs/RichText/utils/formatting.ts +0 -331
|
@@ -1,331 +0,0 @@
|
|
|
1
|
-
import type { EditorState } from '../richTextTypes'
|
|
2
|
-
import { analyzeSelection, restoreSelection, getBlocksBetween } from './selection'
|
|
3
|
-
|
|
4
|
-
export function formatting(state: EditorState) {
|
|
5
|
-
const { doc } = state
|
|
6
|
-
if (!doc) { return { text: () => {}, block: () => {}, list: () => {}, clear: () => {} } }
|
|
7
|
-
|
|
8
|
-
const text = (command: string) => {
|
|
9
|
-
const selection = doc.getSelection()
|
|
10
|
-
if (!selection || !selection.rangeCount) { return }
|
|
11
|
-
|
|
12
|
-
const range = selection.getRangeAt(0)
|
|
13
|
-
const container = range.commonAncestorContainer
|
|
14
|
-
const parentBlock = container.nodeType === 3 ? container.parentElement : container as HTMLElement
|
|
15
|
-
|
|
16
|
-
// Preserve RTL direction when applying text formatting
|
|
17
|
-
const isRTL = parentBlock?.closest('[dir="rtl"]') !== null
|
|
18
|
-
|
|
19
|
-
// Don't apply inline styles directly to block elements
|
|
20
|
-
if (parentBlock?.tagName.match(/^H[1-6]|P|BLOCKQUOTE|LI$/)) {
|
|
21
|
-
if (!range.collapsed && range.toString().trim()) {
|
|
22
|
-
let element: HTMLElement
|
|
23
|
-
if (command === 'underline') { element = doc.createElement('u') }
|
|
24
|
-
else if (command === 'bold') { element = doc.createElement('b') }
|
|
25
|
-
else if (command === 'italic') { element = doc.createElement('i') }
|
|
26
|
-
else { return }
|
|
27
|
-
|
|
28
|
-
if (isRTL) { element.dir = 'rtl' }
|
|
29
|
-
range.surroundContents(element)
|
|
30
|
-
}
|
|
31
|
-
} else {
|
|
32
|
-
if (range.collapsed) { return } // No selection, nothing to format
|
|
33
|
-
|
|
34
|
-
let element: HTMLElement
|
|
35
|
-
if (command === 'bold') { element = doc.createElement('b') }
|
|
36
|
-
else if (command === 'italic') { element = doc.createElement('i') }
|
|
37
|
-
else if (command === 'underline') { element = doc.createElement('u') }
|
|
38
|
-
else { return }
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
range.surroundContents(element)
|
|
42
|
-
} catch {
|
|
43
|
-
// If surroundContents fails (e.g., for selections across multiple nodes)
|
|
44
|
-
// Extract the fragment, wrap it, and insert it back
|
|
45
|
-
const fragment = range.extractContents()
|
|
46
|
-
element.appendChild(fragment)
|
|
47
|
-
range.insertNode(element)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const block = (command: string, tag: string) => {
|
|
53
|
-
const selection = doc.getSelection()
|
|
54
|
-
if (!selection || !selection.rangeCount) { return }
|
|
55
|
-
|
|
56
|
-
const range = selection.getRangeAt(0)
|
|
57
|
-
const container = range.commonAncestorContainer
|
|
58
|
-
const parentBlock = container.nodeType === 3 ? container.parentElement : container as HTMLElement
|
|
59
|
-
const isRTL = parentBlock?.closest('[dir="rtl"]') !== null
|
|
60
|
-
|
|
61
|
-
// Remove any invalid inline style wrapping
|
|
62
|
-
if (parentBlock?.closest('u, b, i')) {
|
|
63
|
-
const wrapper = parentBlock.closest('u, b, i')
|
|
64
|
-
if (wrapper) {
|
|
65
|
-
const parent = wrapper.parentNode
|
|
66
|
-
while (wrapper.firstChild) {
|
|
67
|
-
parent?.insertBefore(wrapper.firstChild, wrapper)
|
|
68
|
-
}
|
|
69
|
-
wrapper.remove()
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Create a new block element of the desired type
|
|
74
|
-
const newBlock = doc.createElement(tag)
|
|
75
|
-
if (isRTL) { newBlock.dir = 'rtl' }
|
|
76
|
-
|
|
77
|
-
// Find the current block to replace
|
|
78
|
-
const currentBlock = parentBlock?.closest('p,h1,h2,h3,h4,h5,h6,blockquote,div') || parentBlock
|
|
79
|
-
|
|
80
|
-
if (currentBlock) {
|
|
81
|
-
// Copy content to the new block
|
|
82
|
-
while (currentBlock.firstChild) {
|
|
83
|
-
newBlock.appendChild(currentBlock.firstChild)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Replace the current block with the new one
|
|
87
|
-
currentBlock.parentNode?.replaceChild(newBlock, currentBlock)
|
|
88
|
-
|
|
89
|
-
// Move cursor into the new block
|
|
90
|
-
range.selectNodeContents(newBlock)
|
|
91
|
-
range.collapse(false)
|
|
92
|
-
selection.removeAllRanges()
|
|
93
|
-
selection.addRange(range)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const list = (command: string) => {
|
|
98
|
-
if (!state.doc || !state.range || !state.selection) { return }
|
|
99
|
-
|
|
100
|
-
const listTag = command === 'orderedList' ? 'ol' : 'ul'
|
|
101
|
-
const selectionInfo = analyzeSelection(state.doc, state.range)
|
|
102
|
-
if (!selectionInfo) { return }
|
|
103
|
-
|
|
104
|
-
const isRTL = selectionInfo.parent.closest('[dir="rtl"]') !== null
|
|
105
|
-
|
|
106
|
-
// If we're inside a list item, handle toggling off or switching list type
|
|
107
|
-
const listItem = selectionInfo.parent.closest('li')
|
|
108
|
-
if (listItem) {
|
|
109
|
-
const listParent = listItem.parentElement
|
|
110
|
-
if (!listParent) { return }
|
|
111
|
-
|
|
112
|
-
// If it's the same list type, toggle off
|
|
113
|
-
if (listParent.tagName.toLowerCase() === listTag) {
|
|
114
|
-
// Create a paragraph from the list item content
|
|
115
|
-
const p = state.doc.createElement('p') as HTMLParagraphElement
|
|
116
|
-
if (isRTL) { p.dir = 'rtl' }
|
|
117
|
-
while (listItem.firstChild) {
|
|
118
|
-
p.appendChild(listItem.firstChild)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
listParent.parentNode?.insertBefore(p, listParent)
|
|
122
|
-
listItem.remove()
|
|
123
|
-
|
|
124
|
-
if (!listParent.querySelector('li')) {
|
|
125
|
-
listParent.remove()
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
restoreSelection(state.doc, state.range, state.selection, selectionInfo, p)
|
|
129
|
-
return
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Convert to the other list type
|
|
133
|
-
const newList = state.doc.createElement(listTag) as HTMLElement
|
|
134
|
-
if (isRTL) { newList.dir = 'rtl' }
|
|
135
|
-
const allItems = Array.from(listParent.children)
|
|
136
|
-
allItems.forEach(item => newList.appendChild(item.cloneNode(true)))
|
|
137
|
-
listParent.parentNode?.replaceChild(newList, listParent)
|
|
138
|
-
|
|
139
|
-
const newListItem = newList.children[Array.from(allItems).indexOf(listItem)]
|
|
140
|
-
restoreSelection(state.doc, state.range, state.selection, selectionInfo, newListItem)
|
|
141
|
-
return
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// For single block conversion
|
|
145
|
-
if (!selectionInfo.isMultiBlock) {
|
|
146
|
-
const li = state.doc.createElement('li')
|
|
147
|
-
const listEl = state.doc.createElement(listTag) as HTMLElement
|
|
148
|
-
if (isRTL) { listEl.dir = 'rtl' }
|
|
149
|
-
|
|
150
|
-
while (selectionInfo.startBlock.firstChild) {
|
|
151
|
-
li.appendChild(selectionInfo.startBlock.firstChild)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
listEl.appendChild(li)
|
|
155
|
-
selectionInfo.startBlock.parentNode?.replaceChild(listEl, selectionInfo.startBlock)
|
|
156
|
-
restoreSelection(state.doc, state.range, state.selection, selectionInfo, li)
|
|
157
|
-
return
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Handle multi-block selection
|
|
161
|
-
const blocks = getBlocksBetween(selectionInfo.startBlock, selectionInfo.endBlock)
|
|
162
|
-
if (!blocks.length) { return }
|
|
163
|
-
|
|
164
|
-
const listEl = state.doc.createElement(listTag) as HTMLElement
|
|
165
|
-
if (isRTL) { listEl.dir = 'rtl' }
|
|
166
|
-
|
|
167
|
-
const firstBlock = blocks[0]
|
|
168
|
-
firstBlock.parentNode?.insertBefore(listEl, firstBlock)
|
|
169
|
-
|
|
170
|
-
blocks.forEach((block) => {
|
|
171
|
-
if (!state.doc) { return }
|
|
172
|
-
const li = state.doc.createElement('li')
|
|
173
|
-
while (block.firstChild) {
|
|
174
|
-
li.appendChild(block.firstChild)
|
|
175
|
-
}
|
|
176
|
-
listEl.appendChild(li)
|
|
177
|
-
block.remove()
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
restoreSelection(state.doc, state.range, state.selection, selectionInfo, listEl)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const clear = () => {
|
|
184
|
-
console.log('[Clear Format] Starting clear format process', state)
|
|
185
|
-
console.assert(!!state, '[Clear Format] State must exist')
|
|
186
|
-
console.assert(!!state.doc, '[Clear Format] Document must exist')
|
|
187
|
-
console.assert(!!state.range, '[Clear Format] Range must exist')
|
|
188
|
-
console.assert(!!state.selection, '[Clear Format] Selection must exist')
|
|
189
|
-
|
|
190
|
-
if (!state.doc || !state.range || !state.selection) {
|
|
191
|
-
console.log('[Clear Format] No document or selection')
|
|
192
|
-
return
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const selectionInfo = analyzeSelection(state.doc, state.range)
|
|
196
|
-
console.log('[Clear Format] Selection info:', selectionInfo)
|
|
197
|
-
if (!selectionInfo) {
|
|
198
|
-
console.log('[Clear Format] No valid selection info')
|
|
199
|
-
return
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// If selection is collapsed (just a cursor), return
|
|
203
|
-
if (state.range.collapsed) {
|
|
204
|
-
console.log('[Clear Format] Selection is collapsed, nothing to clear')
|
|
205
|
-
return
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
console.log('[Clear Format] Processing selection:', {
|
|
209
|
-
startContainer: state.range.startContainer,
|
|
210
|
-
endContainer: state.range.endContainer,
|
|
211
|
-
selectedText: state.range.toString()
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
// Get the selected content
|
|
215
|
-
const fragment = state.range.cloneContents()
|
|
216
|
-
const tempDiv = state.doc.createElement('div')
|
|
217
|
-
tempDiv.appendChild(fragment)
|
|
218
|
-
|
|
219
|
-
console.log('[Clear Format] Original HTML:', tempDiv.innerHTML)
|
|
220
|
-
|
|
221
|
-
// Function to recursively clean a node and its children
|
|
222
|
-
const cleanNode = (node: Node): Node => {
|
|
223
|
-
if (!state.doc) { return node }
|
|
224
|
-
|
|
225
|
-
// Text nodes can be returned as-is
|
|
226
|
-
if (node.nodeType === 3) {
|
|
227
|
-
return node.cloneNode(true)
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (node.nodeType === 1) { // Element node
|
|
231
|
-
const el = node as HTMLElement
|
|
232
|
-
const nodeName = el.nodeName.toLowerCase()
|
|
233
|
-
const inlineTags = ['b', 'i', 'u', 'strong', 'em', 'span', 'font', 'strike', 'sub', 'sup']
|
|
234
|
-
|
|
235
|
-
console.log('[Clear Format] Processing element:', nodeName, {
|
|
236
|
-
hasStyle: el.hasAttribute('style'),
|
|
237
|
-
style: el.getAttribute('style'),
|
|
238
|
-
className: el.className
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
// For inline formatting elements, just extract the text content
|
|
242
|
-
if (inlineTags.includes(nodeName)) {
|
|
243
|
-
// Create a text node with the element's content
|
|
244
|
-
const textContent = el.textContent || ''
|
|
245
|
-
console.log('[Clear Format] Extracting text from inline element:', textContent)
|
|
246
|
-
return state.doc.createTextNode(textContent)
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// For block elements, preserve the element type but remove formatting
|
|
250
|
-
const newEl = state.doc.createElement(nodeName)
|
|
251
|
-
|
|
252
|
-
// Remove any style and class attributes
|
|
253
|
-
if (el.hasAttribute('style')) {
|
|
254
|
-
console.log('[Clear Format] Removing style attribute:', el.getAttribute('style'))
|
|
255
|
-
}
|
|
256
|
-
if (el.className) {
|
|
257
|
-
console.log('[Clear Format] Removing class:', el.className)
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Transfer only specific attributes we want to keep (like href for links)
|
|
261
|
-
if (nodeName === 'a' && el.hasAttribute('href')) {
|
|
262
|
-
newEl.setAttribute('href', el.getAttribute('href') || '')
|
|
263
|
-
if (el.hasAttribute('target')) {
|
|
264
|
-
newEl.setAttribute('target', el.getAttribute('target') || '')
|
|
265
|
-
}
|
|
266
|
-
console.log('[Clear Format] Preserving link attributes')
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// For images, preserve src and alt
|
|
270
|
-
if (nodeName === 'img') {
|
|
271
|
-
if (el.hasAttribute('src')) { newEl.setAttribute('src', el.getAttribute('src') || '') }
|
|
272
|
-
if (el.hasAttribute('alt')) { newEl.setAttribute('alt', el.getAttribute('alt') || '') }
|
|
273
|
-
console.log('[Clear Format] Preserving image attributes')
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Clean and append all child nodes
|
|
277
|
-
Array.from(el.childNodes).forEach((child) => {
|
|
278
|
-
newEl.appendChild(cleanNode(child))
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
return newEl
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return node.cloneNode(true)
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Clean all nodes in the fragment
|
|
288
|
-
const cleanedFragment = state.doc.createDocumentFragment()
|
|
289
|
-
Array.from(tempDiv.childNodes).forEach((node) => {
|
|
290
|
-
cleanedFragment.appendChild(cleanNode(node))
|
|
291
|
-
})
|
|
292
|
-
|
|
293
|
-
// For debugging: check what the cleaned HTML looks like
|
|
294
|
-
const debugDiv = state.doc.createElement('div')
|
|
295
|
-
debugDiv.appendChild(cleanedFragment.cloneNode(true))
|
|
296
|
-
console.log('[Clear Format] Cleaned HTML:', debugDiv.innerHTML)
|
|
297
|
-
|
|
298
|
-
// Replace the content
|
|
299
|
-
state.range.deleteContents()
|
|
300
|
-
state.range.insertNode(cleanedFragment)
|
|
301
|
-
|
|
302
|
-
// Restore selection
|
|
303
|
-
restoreSelection(state.doc, state.range, state.selection, selectionInfo)
|
|
304
|
-
|
|
305
|
-
// Update content state
|
|
306
|
-
state.content = state.doc.body.innerHTML
|
|
307
|
-
console.log('[Clear Format] Updated document HTML:', state.content)
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
return { text, block, list, clear }
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
export function getBlockElement(node: Node): HTMLElement | null {
|
|
314
|
-
const element = node.nodeType === 3 ? node.parentElement : node as HTMLElement
|
|
315
|
-
if (!element) { return null }
|
|
316
|
-
return element.closest('p,h1,h2,h3,h4,h5,h6,blockquote,li') || element
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
export function adjustIndentation(state: EditorState, increase: boolean) {
|
|
320
|
-
const { doc, range } = state
|
|
321
|
-
if (!doc || !range) { return }
|
|
322
|
-
|
|
323
|
-
const blockElement = getBlockElement(range.commonAncestorContainer)
|
|
324
|
-
if (!blockElement) { return }
|
|
325
|
-
|
|
326
|
-
const currentMargin = Number.parseInt(blockElement.style.marginLeft || '0', 10)
|
|
327
|
-
const step = 20 // pixels to indent/outdent
|
|
328
|
-
const newMargin = increase ? currentMargin + step : Math.max(0, currentMargin - step)
|
|
329
|
-
|
|
330
|
-
blockElement.style.marginLeft = `${newMargin}px`
|
|
331
|
-
}
|