jvs-ppt-ui 0.0.16 → 0.0.17

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.
@@ -465,12 +465,15 @@ function applyTextStyleToHtml(html: string, styles: any) {
465
465
  paragraphs.forEach(applyTextStyles)
466
466
  }
467
467
 
468
- // 综合处理字体与样式覆盖:如果指定了 fontFamily,清理/更新所有块级和内联节点的原有硬编码字体
469
- if (styles.fontFamily) {
468
+ // 综合处理字体与样式覆盖:如果指定了 fontFamily 或 fontSize,清理/更新所有块级和内联节点的原有硬编码值
469
+ if (styles.fontFamily || styles.fontSize) {
470
470
  div.querySelectorAll('p, div, li, h1, h2, h3, h4, h5, h6, span, b, strong, i, em, a').forEach((elem: any) => {
471
- if (elem.style && elem.style.fontFamily) {
471
+ if (styles.fontFamily && elem.style && elem.style.fontFamily) {
472
472
  elem.style.fontFamily = styles.fontFamily
473
473
  }
474
+ if (styles.fontSize && elem.style && elem.style.fontSize) {
475
+ elem.style.fontSize = styles.fontSize
476
+ }
474
477
  })
475
478
  }
476
479
 
@@ -571,11 +574,12 @@ function setStyleVal(prop: string, val: any) {
571
574
  }
572
575
  }
573
576
 
574
- if (prop === 'fontFamily') {
577
+ if (prop === 'fontFamily' || prop === 'fontSize') {
575
578
  const container = document.getElementById(`editable-${el.id}`)
576
579
  if (container) {
577
- container.style.fontFamily = val
578
- container.innerHTML = applyTextStyleToHtml(container.innerHTML, { fontFamily: val })
580
+ if (prop === 'fontFamily') container.style.fontFamily = val
581
+ else container.style.fontSize = val
582
+ container.innerHTML = applyTextStyleToHtml(container.innerHTML, { [prop]: val })
579
583
  if (el.type === 'shape') {
580
584
  el.text = container.innerHTML
581
585
  } else {
@@ -11,6 +11,7 @@ import {
11
11
  VideoPlay
12
12
  } from '@element-plus/icons-vue'
13
13
  import { useSlides, DEFAULT_MATERIAL_PAGE_API_URL } from '../composables/useSlides'
14
+ import { isImagePlaceholder } from '../utils/canvas/elementStyles'
14
15
 
15
16
  const {
16
17
  addElement,
@@ -20,7 +21,12 @@ const {
20
21
  materialPageApiUrl,
21
22
  materialHeaders,
22
23
  activeTab,
23
- materialPopoverVisible
24
+ materialPopoverVisible,
25
+ activeSlide,
26
+ selectedElementId,
27
+ placeholderMaterialTargetId,
28
+ resolveActivePlaceholderTargetId,
29
+ replacePlaceholderElement
24
30
  } = useSlides()
25
31
 
26
32
  export interface MaterialItem {
@@ -682,7 +688,7 @@ function formatMaterialTitle(title?: string, fallback = '素材', maxLen = 12):
682
688
  return text.length > maxLen ? `${text.slice(0, maxLen)}...` : text
683
689
  }
684
690
 
685
- // 点击素材卡片插入画布(区分视频与图片/矢量/插画)
691
+ // 点击素材卡片插入画布(区分视频与图片/矢量/插画,若当前处于图片占位符替换状态则优先替换占位符)
686
692
  function handleSelectMaterial(meta: MaterialItem) {
687
693
  const isVid = isVideoMaterial(meta)
688
694
  const targetUrl = meta.sourceUrl || meta.url || meta.downloadUrl || meta.previewUrl
@@ -692,9 +698,36 @@ function handleSelectMaterial(meta: MaterialItem) {
692
698
  return
693
699
  }
694
700
 
695
- let initialDimensions: { width?: number; height?: number } | undefined = undefined
696
701
  const rawW = meta.originalWidth || meta.width
697
702
  const rawH = meta.originalHeight || meta.height
703
+
704
+ const currentSelectedEl = selectedElementId?.value
705
+ ? activeSlide?.value?.elements?.find((el: any) => el.id === selectedElementId.value)
706
+ : null
707
+
708
+ const explicitTargetId =
709
+ placeholderMaterialTargetId?.value ||
710
+ (typeof window !== 'undefined' ? (window as any).__jvsPlaceholderTargetId : null)
711
+
712
+ const targetPlaceholderId =
713
+ (resolveActivePlaceholderTargetId ? resolveActivePlaceholderTargetId() : null) ||
714
+ (explicitTargetId && (!currentSelectedEl || currentSelectedEl.id === explicitTargetId)
715
+ ? explicitTargetId
716
+ : (currentSelectedEl && isImagePlaceholder(currentSelectedEl) ? currentSelectedEl.id : null))
717
+
718
+ if (targetPlaceholderId && replacePlaceholderElement) {
719
+ const replaced = replacePlaceholderElement(targetPlaceholderId, targetUrl, {
720
+ isVideo: isVid,
721
+ width: Number(rawW) || undefined,
722
+ height: Number(rawH) || undefined,
723
+ notifyMessage: isVid
724
+ ? `已使用视频 "${formatMaterialTitle(meta.title, '高清视频', 12)}" 替换占位符`
725
+ : `已使用素材 "${formatMaterialTitle(meta.title, '图片', 12)}" 替换占位符图片`
726
+ })
727
+ if (replaced) return
728
+ }
729
+
730
+ let initialDimensions: { width?: number; height?: number } | undefined = undefined
698
731
  if (rawW && rawH && rawW > 0 && rawH > 0) {
699
732
  const maxWidth = 480
700
733
  const maxHeight = 360
@@ -1,213 +1,214 @@
1
- <script setup lang="ts">
2
- import { computed } from 'vue'
3
- import CanvasTextElement from './CanvasTextElement.vue'
4
- import CanvasShapeElement from './CanvasShapeElement.vue'
5
- import CanvasLineElement from './CanvasLineElement.vue'
6
- import CanvasImageElement from './CanvasImageElement.vue'
7
- import SvgChart from '../../SvgChart.vue'
8
- import { getElementImageSrc, hasElementFill, hasElementBorder, getElementDropShadowFilter } from '../../../utils/canvas/elementStyles'
9
-
10
- const props = defineProps<{
11
- element: any
12
- activeSlide?: any
13
- croppingElementId?: string | null
14
- selectedElementIds?: string[]
15
- selectedElementId?: string | null
16
- isEditingText?: boolean
17
- themeRecipe?: any
18
- getTextStrokeStyle?: (el: any) => string
19
- getElementTextShadow?: (el: any) => string
20
- getRenderedContent?: (el: any) => string
21
- // 直接父盒子的像素尺寸(顶层时为幻灯片逻辑尺寸),用于解析本组 % 样式,
22
- // 再下传给子元素解析它们的 % 宽高。与 PresentationGroupChild 的
23
- // parentW/parentH 锚同一思路:渲染期没有可查的 DOM 地图时用数据链解析。
24
- parentWidthPx?: number
25
- parentHeightPx?: number
26
- }>()
27
-
28
- function resolvePx(val: any, parentPx: number | undefined): number {
29
- if (val === undefined || val === null || val === '' || val === 'auto') return NaN
30
- if (typeof val === 'number') return val
31
- const s = String(val).trim()
32
- if (s.endsWith('%')) {
33
- const pct = parseFloat(s)
34
- const p = Number(parentPx)
35
- if (!isNaN(pct) && isFinite(p) && p > 0) return (pct / 100) * p
36
- return NaN
37
- }
38
- const n = parseFloat(s)
39
- return isNaN(n) ? NaN : n
40
- }
41
-
42
- // 本组盒子自身的像素尺寸:px 样式直接用,% 样式相对父盒解析。
43
- const selfWidthPx = computed(() => {
44
- const resolved = resolvePx((props.element as any)?.style?.width, props.parentWidthPx)
45
- if (isFinite(resolved) && resolved > 0) return resolved
46
- const p = Number(props.parentWidthPx)
47
- return isFinite(p) && p > 0 ? p : NaN
48
- })
49
-
50
- const selfHeightPx = computed(() => {
51
- const resolved = resolvePx((props.element as any)?.style?.height, props.parentHeightPx)
52
- if (isFinite(resolved) && resolved > 0) return resolved
53
- const p = Number(props.parentHeightPx)
54
- return isFinite(p) && p > 0 ? p : NaN
55
- })
56
-
57
- const emit = defineEmits<{
58
- (e: 'childMouseDown', ev: MouseEvent, child: any, parent: any): void
59
- (e: 'childClick', ev: MouseEvent, child: any, parent: any): void
60
- (e: 'childDblClick', ev: MouseEvent, child: any, parent: any): void
61
- (e: 'childContextMenu', ev: MouseEvent, child: any, parent: any): void
62
- (e: 'textFocus', child: any): void
63
- (e: 'textBlur', ev: FocusEvent, child: any): void
64
- (e: 'textInput', ev: Event, child: any): void
65
- (e: 'textKeyDown', ev: KeyboardEvent, child: any): void
66
- (e: 'textMouseMove', ev: MouseEvent, child: any): void
67
- (e: 'textMouseLeave', ev: MouseEvent, child: any): void
68
- (e: 'startEditing', id: string, ev?: MouseEvent): void
69
- (e: 'formulaDblClick', child: any): void
70
- (e: 'tableDblClick', ev: MouseEvent, child: any): void
71
- }>()
72
-
73
- function sortGroupElements(elements: any[]) {
74
- if (!elements || !Array.isArray(elements)) return []
75
- return [...elements].sort((a: any, b: any) => {
76
- const orderA = a.order !== undefined ? a.order : (parseFloat(a.style?.zIndex) || 0)
77
- const orderB = b.order !== undefined ? b.order : (parseFloat(b.style?.zIndex) || 0)
78
- return orderA - orderB
79
- })
80
- }
81
- </script>
82
-
83
- <template>
84
- <div class="group-container-wrapper" style="width: 100%; height: 100%; position: relative; overflow: visible; pointer-events: none;">
85
- <div
86
- v-for="child in sortGroupElements(element.elements || [])"
87
- :key="child.id"
88
- :id="`canvas-el-${child.id}`"
89
- class="group-child-item"
90
- :style="{
91
- position: 'absolute',
92
- left: child.style?.left,
93
- top: child.style?.top,
94
- width: child.style?.width,
95
- height: (child.type === 'text' && (child.autoFit?.type === 'shape' || child.style?.height === 'auto') && !child.content?.includes('<table') && !child.latex && child.style?.writingMode !== 'vertical-rl' && child.style?.writingMode !== 'vertical-lr') ? 'auto' : (child.style?.height || (child.height ? `${child.height}px` : 'auto')),
96
- minHeight: (child.type === 'text' && !child.content?.includes('<table') && !child.latex) ? child.style?.height : undefined,
97
- transform: `rotate(${child.style?.rotate || 0}deg) scale(${child.type !== 'text' && child.type !== 'line' && ((child.style as any)?.flipH || child.flipH || child.isFlipH) ? -1 : 1}, ${child.type !== 'text' && child.type !== 'line' && ((child.style as any)?.flipV || child.flipV || child.isFlipV) ? -1 : 1})`,
98
- opacity: child.style?.opacity !== undefined ? child.style.opacity : 1,
99
- filter: getElementDropShadowFilter(child),
100
- pointerEvents: child.type === 'group' ? 'none' : (((child.type === 'shape' || !child.type) && !hasElementFill(child) && !hasElementBorder(child)) ? 'none' : 'auto')
101
- }"
102
- @mousedown.stop="emit('childMouseDown', $event, child, element)"
103
- @click.stop="emit('childClick', $event, child, element)"
104
- @dblclick.stop="emit('childDblClick', $event, child, element)"
105
- @contextmenu.stop.prevent="emit('childContextMenu', $event, child, element)"
106
- >
107
- <!-- Child Text -->
108
- <template v-if="child.type === 'text'">
109
- <CanvasTextElement
110
- :element="child"
111
- :active-slide="activeSlide"
112
- :is-selected="selectedElementId === child.id"
113
- :is-editing-text="isEditingText"
114
- :selected-element-id="selectedElementId"
115
- :theme-recipe="themeRecipe"
116
- :rendered-content="getRenderedContent ? getRenderedContent(child) : child.content"
117
- :get-text-stroke-style="getTextStrokeStyle"
118
- :get-element-text-shadow="getElementTextShadow"
119
- @focusin="emit('textFocus', child)"
120
- @focusout="emit('textBlur', $event, child)"
121
- @input="emit('textInput', $event, child)"
122
- @keydown="emit('textKeyDown', $event, child)"
123
- @mousemove="emit('textMouseMove', $event, child)"
124
- @mouseleave="emit('textMouseLeave', $event, child)"
125
- @dblclick="emit('startEditing', child.id)"
126
- @formula-dbl-click="emit('formulaDblClick', child)"
127
- @table-dbl-click="emit('tableDblClick', $event, child)"
128
- />
129
- </template>
130
-
131
- <!-- Child Shape -->
132
- <template v-else-if="child.type === 'shape'">
133
- <CanvasShapeElement
134
- :element="child"
135
- :active-slide="activeSlide"
136
- :is-selected="selectedElementId === child.id"
137
- :is-editing-text="isEditingText"
138
- :selected-element-id="selectedElementId"
139
- :theme-recipe="themeRecipe"
140
- :parent-width-px="selfWidthPx"
141
- :parent-height-px="selfHeightPx"
142
- :get-text-stroke-style="getTextStrokeStyle"
143
- :get-element-text-shadow="getElementTextShadow"
144
- @text-focus="emit('textFocus', child)"
145
- @text-blur="emit('textBlur', $event, child)"
146
- @text-input="emit('textInput', $event, child)"
147
- @text-key-down="emit('textKeyDown', $event, child)"
148
- @start-editing="(ev) => emit('startEditing', child.id, ev)"
149
- />
150
- </template>
151
-
152
- <!-- Child Line -->
153
- <template v-else-if="child.type === 'line'">
154
- <CanvasLineElement :element="child" :all-elements="element.elements" />
155
- </template>
156
-
157
- <!-- Child Image -->
158
- <template
159
- v-else-if="child.type === 'image' || child.type === 'pic' || child.type === 'picture' || (child.type !== 'text' && child.type !== 'line' && child.type !== 'shape' && child.type !== 'group' && child.type !== 'chart' && Boolean(getElementImageSrc(child)))">
160
- <CanvasImageElement :element="child" :cropping-element-id="croppingElementId" />
161
- </template>
162
-
163
- <!-- Child Chart -->
164
- <template v-else-if="child.type === 'chart'">
165
- <div style="width: 100%; height: 100%; overflow: visible; box-sizing: border-box;" :style="{
166
- backgroundColor: child.style?.fill || 'transparent',
167
- borderWidth: child.style?.borderWidth || '0px',
168
- borderStyle: child.style?.borderStyle || 'solid',
169
- borderColor: child.style?.borderColor || 'transparent',
170
- borderRadius: child.style?.borderRadius || '0px',
171
- boxShadow: child.style?.boxShadow || 'none'
172
- }">
173
- <SvgChart :element="child" />
174
- </div>
175
- </template>
176
-
177
- <!-- Nested Group -->
178
- <template v-else-if="child.type === 'group'">
179
- <CanvasGroupElement
180
- :element="child"
181
- :active-slide="activeSlide"
182
- :cropping-element-id="croppingElementId"
183
- :parent-width-px="selfWidthPx"
184
- :parent-height-px="selfHeightPx"
185
- :selected-element-ids="selectedElementIds"
186
- :selected-element-id="selectedElementId"
187
- :is-editing-text="isEditingText"
188
- :theme-recipe="themeRecipe"
189
- :get-text-stroke-style="getTextStrokeStyle"
190
- :get-element-text-shadow="getElementTextShadow"
191
- :get-rendered-content="getRenderedContent"
192
- @child-mouse-down="(ev, c, p) => emit('childMouseDown', ev, c, p)"
193
- @child-click="(ev, c, p) => emit('childClick', ev, c, p)"
194
- @child-dbl-click="(ev, c, p) => emit('childDblClick', ev, c, p)"
195
- @child-context-menu="(ev, c, p) => emit('childContextMenu', ev, c, p)"
196
- @text-focus="(c) => emit('textFocus', c)"
197
- @text-blur="(ev, c) => emit('textBlur', ev, c)"
198
- @text-input="(ev, c) => emit('textInput', ev, c)"
199
- @text-key-down="(ev, c) => emit('textKeyDown', ev, c)"
200
- @text-mouse-move="(ev, c) => emit('textMouseMove', ev, c)"
201
- @text-mouse-leave="(ev, c) => emit('textMouseLeave', ev, c)"
202
- @start-editing="(id, ev) => emit('startEditing', id, ev)"
203
- />
204
- </template>
205
- </div>
206
- </div>
207
- </template>
208
-
209
- <style scoped>
210
- .group-child-item {
211
- box-sizing: border-box;
212
- }
213
- </style>
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import CanvasTextElement from './CanvasTextElement.vue'
4
+ import CanvasShapeElement from './CanvasShapeElement.vue'
5
+ import CanvasLineElement from './CanvasLineElement.vue'
6
+ import CanvasImageElement from './CanvasImageElement.vue'
7
+ import SvgChart from '../../SvgChart.vue'
8
+ import { getElementImageSrc, hasElementFill, hasElementBorder, getElementDropShadowFilter, isImagePlaceholder } from '../../../utils/canvas/elementStyles'
9
+
10
+ const props = defineProps<{
11
+ element: any
12
+ activeSlide?: any
13
+ croppingElementId?: string | null
14
+ selectedElementIds?: string[]
15
+ selectedElementId?: string | null
16
+ isEditingText?: boolean
17
+ themeRecipe?: any
18
+ getTextStrokeStyle?: (el: any) => string
19
+ getElementTextShadow?: (el: any) => string
20
+ getRenderedContent?: (el: any) => string
21
+ // 直接父盒子的像素尺寸(顶层时为幻灯片逻辑尺寸),用于解析本组 % 样式,
22
+ // 再下传给子元素解析它们的 % 宽高。与 PresentationGroupChild 的
23
+ // parentW/parentH 锚同一思路:渲染期没有可查的 DOM 地图时用数据链解析。
24
+ parentWidthPx?: number
25
+ parentHeightPx?: number
26
+ }>()
27
+
28
+ function resolvePx(val: any, parentPx: number | undefined): number {
29
+ if (val === undefined || val === null || val === '' || val === 'auto') return NaN
30
+ if (typeof val === 'number') return val
31
+ const s = String(val).trim()
32
+ if (s.endsWith('%')) {
33
+ const pct = parseFloat(s)
34
+ const p = Number(parentPx)
35
+ if (!isNaN(pct) && isFinite(p) && p > 0) return (pct / 100) * p
36
+ return NaN
37
+ }
38
+ const n = parseFloat(s)
39
+ return isNaN(n) ? NaN : n
40
+ }
41
+
42
+ // 本组盒子自身的像素尺寸:px 样式直接用,% 样式相对父盒解析。
43
+ const selfWidthPx = computed(() => {
44
+ const resolved = resolvePx((props.element as any)?.style?.width, props.parentWidthPx)
45
+ if (isFinite(resolved) && resolved > 0) return resolved
46
+ const p = Number(props.parentWidthPx)
47
+ return isFinite(p) && p > 0 ? p : NaN
48
+ })
49
+
50
+ const selfHeightPx = computed(() => {
51
+ const resolved = resolvePx((props.element as any)?.style?.height, props.parentHeightPx)
52
+ if (isFinite(resolved) && resolved > 0) return resolved
53
+ const p = Number(props.parentHeightPx)
54
+ return isFinite(p) && p > 0 ? p : NaN
55
+ })
56
+
57
+ const emit = defineEmits<{
58
+ (e: 'childMouseDown', ev: MouseEvent, child: any, parent: any): void
59
+ (e: 'childClick', ev: MouseEvent, child: any, parent: any): void
60
+ (e: 'childDblClick', ev: MouseEvent, child: any, parent: any): void
61
+ (e: 'childContextMenu', ev: MouseEvent, child: any, parent: any): void
62
+ (e: 'textFocus', child: any): void
63
+ (e: 'textBlur', ev: FocusEvent, child: any): void
64
+ (e: 'textInput', ev: Event, child: any): void
65
+ (e: 'textKeyDown', ev: KeyboardEvent, child: any): void
66
+ (e: 'textMouseMove', ev: MouseEvent, child: any): void
67
+ (e: 'textMouseLeave', ev: MouseEvent, child: any): void
68
+ (e: 'startEditing', id: string, ev?: MouseEvent): void
69
+ (e: 'formulaDblClick', child: any): void
70
+ (e: 'tableDblClick', ev: MouseEvent, child: any): void
71
+ }>()
72
+
73
+ function sortGroupElements(elements: any[]) {
74
+ if (!elements || !Array.isArray(elements)) return []
75
+ return [...elements].sort((a: any, b: any) => {
76
+ const orderA = a.order !== undefined ? a.order : (parseFloat(a.style?.zIndex) || 0)
77
+ const orderB = b.order !== undefined ? b.order : (parseFloat(b.style?.zIndex) || 0)
78
+ return orderA - orderB
79
+ })
80
+ }
81
+ </script>
82
+
83
+ <template>
84
+ <div class="group-container-wrapper" style="width: 100%; height: 100%; position: relative; overflow: visible; pointer-events: none;">
85
+ <div
86
+ v-for="child in sortGroupElements(element.elements || [])"
87
+ :key="child.id"
88
+ :id="`canvas-el-${child.id}`"
89
+ class="group-child-item"
90
+ :style="{
91
+ position: 'absolute',
92
+ left: child.style?.left,
93
+ top: child.style?.top,
94
+ width: child.style?.width,
95
+ height: (child.type === 'text' && (child.autoFit?.type === 'shape' || child.style?.height === 'auto') && !child.content?.includes('<table') && !child.latex && child.style?.writingMode !== 'vertical-rl' && child.style?.writingMode !== 'vertical-lr') ? 'auto' : (child.style?.height || (child.height ? `${child.height}px` : 'auto')),
96
+ minHeight: (child.type === 'text' && !child.content?.includes('<table') && !child.latex) ? child.style?.height : undefined,
97
+ transform: `rotate(${child.style?.rotate || 0}deg) scale(${child.type !== 'text' && child.type !== 'line' && ((child.style as any)?.flipH || child.flipH || child.isFlipH) ? -1 : 1}, ${child.type !== 'text' && child.type !== 'line' && ((child.style as any)?.flipV || child.flipV || child.isFlipV) ? -1 : 1})`,
98
+ opacity: child.style?.opacity !== undefined ? child.style.opacity : 1,
99
+ filter: getElementDropShadowFilter(child),
100
+ pointerEvents: child.type === 'group' ? 'none' : (((child.type === 'shape' || !child.type) && !hasElementFill(child) && !hasElementBorder(child)) ? 'none' : 'auto')
101
+ }"
102
+ @mousedown.stop="emit('childMouseDown', $event, child, element)"
103
+ @click.stop="emit('childClick', $event, child, element)"
104
+ @dblclick.stop="emit('childDblClick', $event, child, element)"
105
+ @contextmenu.stop.prevent="emit('childContextMenu', $event, child, element)"
106
+ >
107
+ <!-- Child Text -->
108
+ <template v-if="child.type === 'text'">
109
+ <CanvasTextElement
110
+ :element="child"
111
+ :active-slide="activeSlide"
112
+ :is-selected="selectedElementId === child.id"
113
+ :is-editing-text="isEditingText"
114
+ :selected-element-id="selectedElementId"
115
+ :theme-recipe="themeRecipe"
116
+ :rendered-content="getRenderedContent ? getRenderedContent(child) : child.content"
117
+ :get-text-stroke-style="getTextStrokeStyle"
118
+ :get-element-text-shadow="getElementTextShadow"
119
+ @focusin="emit('textFocus', child)"
120
+ @focusout="emit('textBlur', $event, child)"
121
+ @input="emit('textInput', $event, child)"
122
+ @keydown="emit('textKeyDown', $event, child)"
123
+ @mousemove="emit('textMouseMove', $event, child)"
124
+ @mouseleave="emit('textMouseLeave', $event, child)"
125
+ @dblclick="emit('startEditing', child.id)"
126
+ @formula-dbl-click="emit('formulaDblClick', child)"
127
+ @table-dbl-click="emit('tableDblClick', $event, child)"
128
+ />
129
+ </template>
130
+
131
+ <!-- Child Shape -->
132
+ <template v-else-if="child.type === 'shape'">
133
+ <CanvasShapeElement
134
+ :element="child"
135
+ :active-slide="activeSlide"
136
+ :is-selected="selectedElementId === child.id"
137
+ :is-editing-text="isEditingText"
138
+ :selected-element-id="selectedElementId"
139
+ :theme-recipe="themeRecipe"
140
+ :parent-width-px="selfWidthPx"
141
+ :parent-height-px="selfHeightPx"
142
+ :get-text-stroke-style="getTextStrokeStyle"
143
+ :get-element-text-shadow="getElementTextShadow"
144
+ :is-image-placeholder="isImagePlaceholder"
145
+ @text-focus="emit('textFocus', child)"
146
+ @text-blur="emit('textBlur', $event, child)"
147
+ @text-input="emit('textInput', $event, child)"
148
+ @text-key-down="emit('textKeyDown', $event, child)"
149
+ @start-editing="(ev) => emit('startEditing', child.id, ev)"
150
+ />
151
+ </template>
152
+
153
+ <!-- Child Line -->
154
+ <template v-else-if="child.type === 'line'">
155
+ <CanvasLineElement :element="child" :all-elements="element.elements" />
156
+ </template>
157
+
158
+ <!-- Child Image -->
159
+ <template
160
+ v-else-if="child.type === 'image' || child.type === 'pic' || child.type === 'picture' || (child.type !== 'text' && child.type !== 'line' && child.type !== 'shape' && child.type !== 'group' && child.type !== 'chart' && Boolean(getElementImageSrc(child)))">
161
+ <CanvasImageElement :element="child" :cropping-element-id="croppingElementId" />
162
+ </template>
163
+
164
+ <!-- Child Chart -->
165
+ <template v-else-if="child.type === 'chart'">
166
+ <div style="width: 100%; height: 100%; overflow: visible; box-sizing: border-box;" :style="{
167
+ backgroundColor: child.style?.fill || 'transparent',
168
+ borderWidth: child.style?.borderWidth || '0px',
169
+ borderStyle: child.style?.borderStyle || 'solid',
170
+ borderColor: child.style?.borderColor || 'transparent',
171
+ borderRadius: child.style?.borderRadius || '0px',
172
+ boxShadow: child.style?.boxShadow || 'none'
173
+ }">
174
+ <SvgChart :element="child" />
175
+ </div>
176
+ </template>
177
+
178
+ <!-- Nested Group -->
179
+ <template v-else-if="child.type === 'group'">
180
+ <CanvasGroupElement
181
+ :element="child"
182
+ :active-slide="activeSlide"
183
+ :cropping-element-id="croppingElementId"
184
+ :parent-width-px="selfWidthPx"
185
+ :parent-height-px="selfHeightPx"
186
+ :selected-element-ids="selectedElementIds"
187
+ :selected-element-id="selectedElementId"
188
+ :is-editing-text="isEditingText"
189
+ :theme-recipe="themeRecipe"
190
+ :get-text-stroke-style="getTextStrokeStyle"
191
+ :get-element-text-shadow="getElementTextShadow"
192
+ :get-rendered-content="getRenderedContent"
193
+ @child-mouse-down="(ev, c, p) => emit('childMouseDown', ev, c, p)"
194
+ @child-click="(ev, c, p) => emit('childClick', ev, c, p)"
195
+ @child-dbl-click="(ev, c, p) => emit('childDblClick', ev, c, p)"
196
+ @child-context-menu="(ev, c, p) => emit('childContextMenu', ev, c, p)"
197
+ @text-focus="(c) => emit('textFocus', c)"
198
+ @text-blur="(ev, c) => emit('textBlur', ev, c)"
199
+ @text-input="(ev, c) => emit('textInput', ev, c)"
200
+ @text-key-down="(ev, c) => emit('textKeyDown', ev, c)"
201
+ @text-mouse-move="(ev, c) => emit('textMouseMove', ev, c)"
202
+ @text-mouse-leave="(ev, c) => emit('textMouseLeave', ev, c)"
203
+ @start-editing="(id, ev) => emit('startEditing', id, ev)"
204
+ />
205
+ </template>
206
+ </div>
207
+ </div>
208
+ </template>
209
+
210
+ <style scoped>
211
+ .group-child-item {
212
+ box-sizing: border-box;
213
+ }
214
+ </style>