jvs-ppt-ui 0.0.15 → 0.0.16

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 (27) hide show
  1. package/dist/lib/jvs-ppt-ui.es.js +20301 -19823
  2. package/dist/lib/jvs-ppt-ui.umd.js +99 -99
  3. package/dist/lib/style.css +1 -1
  4. package/package.json +1 -1
  5. package/src/components/CanvasArea.vue +14 -115
  6. package/src/components/ChartTypeSelector.vue +34 -30
  7. package/src/components/Header.vue +40 -5
  8. package/src/components/PresentationGroupChild.vue +44 -13
  9. package/src/components/PresentationMode.vue +84 -33
  10. package/src/components/SidebarLeft.vue +96 -18
  11. package/src/components/SidebarRight.vue +6 -3
  12. package/src/components/Toolbar.vue +1 -1
  13. package/src/components/canvas/elements/CanvasShapeElement.vue +91 -34
  14. package/src/components/sidebar/common/SidebarBorderAccordion.vue +2 -2
  15. package/src/components/sidebar/common/SidebarReflectionAccordion.vue +2 -2
  16. package/src/components/sidebar/common/SidebarShadowAccordion.vue +2 -2
  17. package/src/components/sidebar/panels/SidebarChartFormatPanel.vue +3 -3
  18. package/src/components/sidebar/panels/SidebarDesignPanel.vue +170 -9
  19. package/src/components/sidebar/panels/SidebarShapeFormatPanel.vue +176 -16
  20. package/src/components/sidebar/panels/SidebarTableFormatPanel.vue +2 -2
  21. package/src/components/sidebar/panels/SidebarTextFormatPanel.vue +2 -2
  22. package/src/components/sidebar/panels/SidebarTransitionPanel.vue +30 -19
  23. package/src/composables/canvas/useCanvasTransform.ts +24 -0
  24. package/src/composables/sidebar/useSidebarShapeFormat.ts +248 -17
  25. package/src/composables/useSlides.ts +12 -0
  26. package/src/style.css +4 -2
  27. package/src/utils/canvas/elementStyles.ts +22 -0
@@ -16,7 +16,9 @@ import {
16
16
  hasElementBorderGradient,
17
17
  getElementBorderGradientCss,
18
18
  getGradientAngleDeg,
19
- getSvgGradientCoords
19
+ getSvgGradientCoords,
20
+ isElementPatternTile,
21
+ getElementPatternTileSize
20
22
  } from '../utils/canvas/elementStyles'
21
23
  import {
22
24
  getLineElementPath, getLineElementRenderPath, getLineDashArray, getSvgWidth, getSvgHeight, isEllipseShape
@@ -532,6 +534,22 @@ function getElementWidthHeight(el: any) {
532
534
  return { width, height }
533
535
  }
534
536
 
537
+ function getShapePatternTransform(el: any, isTriangle = false): string | undefined {
538
+ if (!isElementPatternTile(el)) return undefined
539
+ const dim = getElementWidthHeight(el)
540
+ const w = Math.max(1, dim.width || 100)
541
+ const h = Math.max(1, dim.height || 100)
542
+ if (isTriangle) {
543
+ return `scale(${+(100 / w).toFixed(4)}, ${+(100 / h).toFixed(4)})`
544
+ }
545
+ const vbW = el.viewBox ? el.viewBox[0] : 200
546
+ const vbH = el.viewBox ? el.viewBox[1] : 200
547
+ const bw = el.shapType === 'arc' ? 0 : (el.style?.borderWidth ? parseFloat(el.style.borderWidth) : 2)
548
+ const effW = Math.max(1, w - bw)
549
+ const effH = Math.max(1, h - bw)
550
+ return `scale(${+(vbW / effW).toFixed(4)}, ${+(vbH / effH).toFixed(4)})`
551
+ }
552
+
535
553
  function parseColorString(fill: any, fallback = 'none'): string {
536
554
  if (!fill) return fallback
537
555
  if (typeof fill === 'string') return fill
@@ -1120,14 +1138,17 @@ function getSafeShapeText(el: any): string {
1120
1138
  <svg v-if="el.path || (el.content && (el.content.trim().startsWith('M') || el.content.trim().startsWith('m')))"
1121
1139
  :viewBox="`0 0 ${el.viewBox ? el.viewBox[0] : (el.width || 200)} ${el.viewBox ? el.viewBox[1] : (el.height || 200)}`"
1122
1140
  preserveAspectRatio="none" style="width: 100%; height: 100%; display: block; overflow: visible;">
1123
- <defs v-if="getElementPattern(el)">
1124
- <pattern :id="`pres-master-pat-${slide.id || activeIndex}-${el.id}`" width="100%" height="100%" patternContentUnits="objectBoundingBox">
1125
- <image :href="getElementPattern(el)" width="1" height="1" preserveAspectRatio="none" />
1126
- </pattern>
1127
- </defs>
1128
1141
  <defs>
1129
- <pattern v-if="getElementPattern(el)" :id="`pres-master-pat-${slide.id || activeIndex}-${el.id}`" width="100%" height="100%" patternContentUnits="objectBoundingBox">
1130
- <image :href="getElementPattern(el)" width="1" height="1" preserveAspectRatio="none" />
1142
+ <pattern v-if="getElementPattern(el)" :id="`pres-master-pat-${slide.id || activeIndex}-${el.id}`"
1143
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : '100%'"
1144
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : '100%'"
1145
+ :patternUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
1146
+ :patternContentUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
1147
+ :patternTransform="getShapePatternTransform(el, false)">
1148
+ <image :href="getElementPattern(el)"
1149
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : 1"
1150
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : 1"
1151
+ preserveAspectRatio="none" />
1131
1152
  </pattern>
1132
1153
  <template v-if="getElementGradientConfig(el)">
1133
1154
  <radialGradient v-if="getElementGradientConfig(el)!.path && getElementGradientConfig(el)!.path !== 'line'"
@@ -1169,7 +1190,9 @@ function getSafeShapeText(el: any): string {
1169
1190
  <div v-else :style="{
1170
1191
  backgroundColor: getElementPattern(el) ? 'transparent' : ((getElementGradientCss(el) || (getElementFill(el) !== undefined && getElementFill(el) !== 'none')) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#3b82f6')),
1171
1192
  backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
1172
- backgroundSize: getElementPattern(el) ? 'cover' : undefined,
1193
+ backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
1194
+ backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
1195
+ backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
1173
1196
  width: '100%',
1174
1197
  height: '100%',
1175
1198
  borderRadius: el.style?.borderRadius || '0px',
@@ -1321,9 +1344,9 @@ function getSafeShapeText(el: any): string {
1321
1344
  height: '100%',
1322
1345
  backgroundColor: getElementPattern(el) ? 'transparent' : ((getElementGradientCss(el) || (getElementFill(el) !== undefined && getElementFill(el) !== 'none')) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#3b82f6')),
1323
1346
  backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
1324
- backgroundSize: getElementPattern(el) ? 'cover' : undefined,
1325
- backgroundPosition: getElementPattern(el) ? 'center center' : undefined,
1326
- backgroundRepeat: getElementPattern(el) ? 'no-repeat' : undefined,
1347
+ backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
1348
+ backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
1349
+ backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
1327
1350
  borderRadius: '50%',
1328
1351
  borderWidth: `${getElementBorderWidth(el)}px`,
1329
1352
  borderStyle: el.style?.borderStyle || (el as any).borderType || 'solid',
@@ -1337,9 +1360,16 @@ function getSafeShapeText(el: any): string {
1337
1360
  style="width: 100%; height: 100%; display: block; overflow: visible;"
1338
1361
  :style="{ filter: (el.shadow && el.shadow.type === 'inner') ? `url(#pres-shape-inner-shadow-${el.id})` : '' }">
1339
1362
  <defs>
1340
- <pattern v-if="getElementPattern(el)" :id="`pres-main-shape-pattern-${el.id}`" width="100%" height="100%"
1341
- patternContentUnits="objectBoundingBox">
1342
- <image :href="getElementPattern(el)" width="1" height="1" preserveAspectRatio="none" />
1363
+ <pattern v-if="getElementPattern(el)" :id="`pres-main-shape-pattern-${el.id}`"
1364
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : '100%'"
1365
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : '100%'"
1366
+ :patternUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
1367
+ :patternContentUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
1368
+ :patternTransform="getShapePatternTransform(el, true)">
1369
+ <image :href="getElementPattern(el)"
1370
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : 1"
1371
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : 1"
1372
+ preserveAspectRatio="none" />
1343
1373
  </pattern>
1344
1374
  <template v-if="getElementGradientConfig(el)">
1345
1375
  <radialGradient
@@ -1394,9 +1424,16 @@ function getSafeShapeText(el: any): string {
1394
1424
  <svg overflow="visible" style="width: 100%; height: 100%; display: block; overflow: visible;"
1395
1425
  :style="{ filter: (el.shadow && el.shadow.type === 'inner') ? `url(#pres-shape-inner-shadow-${el.id})` : '' }">
1396
1426
  <defs>
1397
- <pattern v-if="getElementPattern(el)" :id="`pres-main-shape-pattern-${el.id}`" width="100%" height="100%"
1398
- patternContentUnits="objectBoundingBox">
1399
- <image :href="getElementPattern(el)" width="1" height="1" preserveAspectRatio="none" />
1427
+ <pattern v-if="getElementPattern(el)" :id="`pres-main-shape-pattern-${el.id}`"
1428
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : '100%'"
1429
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : '100%'"
1430
+ :patternUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
1431
+ :patternContentUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
1432
+ :patternTransform="getShapePatternTransform(el, false)">
1433
+ <image :href="getElementPattern(el)"
1434
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : 1"
1435
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : 1"
1436
+ preserveAspectRatio="none" />
1400
1437
  </pattern>
1401
1438
  <template v-if="getElementGradientConfig(el)">
1402
1439
  <radialGradient
@@ -1453,9 +1490,9 @@ function getSafeShapeText(el: any): string {
1453
1490
  height: '100%',
1454
1491
  backgroundColor: getElementPattern(el) ? 'transparent' : ((getElementGradientCss(el) || (getElementFill(el) !== undefined && getElementFill(el) !== 'none')) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#10b981')),
1455
1492
  backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
1456
- backgroundSize: getElementPattern(el) ? 'cover' : undefined,
1457
- backgroundPosition: getElementPattern(el) ? 'center center' : undefined,
1458
- backgroundRepeat: getElementPattern(el) ? 'no-repeat' : undefined,
1493
+ backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
1494
+ backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
1495
+ backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
1459
1496
  borderRadius: el.style?.borderRadius !== undefined ? el.style.borderRadius : '0px',
1460
1497
  borderWidth: `${getElementBorderWidth(el)}px`,
1461
1498
  borderStyle: el.style?.borderStyle || (el as any).borderType || 'solid',
@@ -1765,9 +1802,9 @@ function getSafeShapeText(el: any): string {
1765
1802
  height: '100%',
1766
1803
  backgroundColor: getElementPattern(el) ? 'transparent' : ((getElementGradientCss(el) || (getElementFill(el) !== undefined && getElementFill(el) !== 'none')) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#3b82f6')),
1767
1804
  backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
1768
- backgroundSize: getElementPattern(el) ? 'cover' : undefined,
1769
- backgroundPosition: getElementPattern(el) ? 'center center' : undefined,
1770
- backgroundRepeat: getElementPattern(el) ? 'no-repeat' : undefined,
1805
+ backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
1806
+ backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
1807
+ backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
1771
1808
  borderRadius: '50%',
1772
1809
  borderWidth: `${getElementBorderWidth(el)}px`,
1773
1810
  borderStyle: el.style?.borderStyle || (el as any).borderType || 'solid',
@@ -1781,9 +1818,16 @@ function getSafeShapeText(el: any): string {
1781
1818
  style="width: 100%; height: 100%; display: block; overflow: visible;"
1782
1819
  :style="{ filter: (el.shadow && el.shadow.type === 'inner') ? `url(#pres-shape-inner-shadow-${el.id})` : '' }">
1783
1820
  <defs>
1784
- <pattern v-if="getElementPattern(el)" :id="`pres-shape-pattern-${el.id}`" width="100%" height="100%"
1785
- patternContentUnits="objectBoundingBox">
1786
- <image :href="getElementPattern(el)" width="1" height="1" preserveAspectRatio="none" />
1821
+ <pattern v-if="getElementPattern(el)" :id="`pres-shape-pattern-${el.id}`"
1822
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : '100%'"
1823
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : '100%'"
1824
+ :patternUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
1825
+ :patternContentUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
1826
+ :patternTransform="getShapePatternTransform(el, true)">
1827
+ <image :href="getElementPattern(el)"
1828
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : 1"
1829
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : 1"
1830
+ preserveAspectRatio="none" />
1787
1831
  </pattern>
1788
1832
  <template v-if="getElementGradientConfig(el)">
1789
1833
  <radialGradient
@@ -1838,9 +1882,16 @@ function getSafeShapeText(el: any): string {
1838
1882
  <svg overflow="visible" style="width: 100%; height: 100%; display: block; overflow: visible;"
1839
1883
  :style="{ filter: (el.shadow && el.shadow.type === 'inner') ? `url(#pres-shape-inner-shadow-${el.id})` : '' }">
1840
1884
  <defs>
1841
- <pattern v-if="getElementPattern(el)" :id="`pres-shape-pattern-${el.id}`" width="100%" height="100%"
1842
- patternContentUnits="objectBoundingBox">
1843
- <image :href="getElementPattern(el)" width="1" height="1" preserveAspectRatio="none" />
1885
+ <pattern v-if="getElementPattern(el)" :id="`pres-shape-pattern-${el.id}`"
1886
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : '100%'"
1887
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : '100%'"
1888
+ :patternUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
1889
+ :patternContentUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
1890
+ :patternTransform="getShapePatternTransform(el, false)">
1891
+ <image :href="getElementPattern(el)"
1892
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : 1"
1893
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : 1"
1894
+ preserveAspectRatio="none" />
1844
1895
  </pattern>
1845
1896
  <template v-if="getElementGradientConfig(el)">
1846
1897
  <radialGradient
@@ -1897,9 +1948,9 @@ function getSafeShapeText(el: any): string {
1897
1948
  height: '100%',
1898
1949
  backgroundColor: getElementPattern(el) ? 'transparent' : ((getElementGradientCss(el) || getElementFill(el) !== undefined) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#10b981')),
1899
1950
  backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
1900
- backgroundSize: getElementPattern(el) ? 'cover' : undefined,
1901
- backgroundPosition: getElementPattern(el) ? 'center center' : undefined,
1902
- backgroundRepeat: getElementPattern(el) ? 'no-repeat' : undefined,
1951
+ backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
1952
+ backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
1953
+ backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
1903
1954
  borderRadius: '4px',
1904
1955
  borderWidth: `${getElementBorderWidth(el)}px`,
1905
1956
  borderStyle: el.style?.borderStyle || (el as any).borderType || 'solid',
@@ -20,7 +20,9 @@ import {
20
20
  getElementBorderGradientCss,
21
21
  getGradientAngleDeg,
22
22
  getSvgGradientCoords,
23
- isImagePlaceholder
23
+ isImagePlaceholder,
24
+ isElementPatternTile,
25
+ getElementPatternTileSize
24
26
  } from '../utils/canvas/elementStyles'
25
27
  import CanvasImageElement from './canvas/elements/CanvasImageElement.vue'
26
28
 
@@ -75,29 +77,59 @@ function observeThumbItem(el: HTMLElement | null, index: number) {
75
77
  if (!el || typeof IntersectionObserver === 'undefined') return
76
78
  if (!thumbObserver) {
77
79
  thumbObserver = new IntersectionObserver((entries) => {
80
+ let mutated = false
78
81
  entries.forEach(entry => {
79
82
  if (entry.isIntersecting) {
80
83
  const idx = Number((entry.target as HTMLElement).dataset.slideIndex)
81
84
  if (!isNaN(idx) && !renderedThumbIndexes.value.has(idx)) {
82
85
  renderedThumbIndexes.value.add(idx)
86
+ mutated = true
83
87
  }
84
88
  }
85
89
  })
90
+ if (mutated) {
91
+ renderedThumbIndexes.value = new Set(renderedThumbIndexes.value)
92
+ }
86
93
  }, {
87
94
  rootMargin: '300px 0px'
88
95
  })
89
96
  }
90
- thumbObserver.observe(el)
97
+ if (!renderedThumbIndexes.value.has(index)) {
98
+ thumbObserver.observe(el)
99
+ }
91
100
  }
92
101
 
93
102
  watch(activeIndex, (newIdx) => {
94
103
  if (!renderedThumbIndexes.value.has(newIdx)) {
95
- renderedThumbIndexes.value.add(newIdx)
104
+ const next = new Set(renderedThumbIndexes.value)
105
+ next.add(newIdx)
106
+ renderedThumbIndexes.value = next
96
107
  }
97
108
  })
98
109
 
99
- watch(() => slides.value.length, () => {
100
- renderedThumbIndexes.value = new Set([0, 1, 2, 3, 4, 5, activeIndex.value])
110
+ watch(() => slides.value.length, (newLen, oldLen) => {
111
+ const delta = typeof oldLen === 'number' ? newLen - oldLen : 0
112
+ const isBulkImport = typeof oldLen === 'number' && delta > 5 && activeIndex.value === 0
113
+ if (isBulkImport) {
114
+ if (thumbObserver) {
115
+ thumbObserver.disconnect()
116
+ thumbObserver = null
117
+ }
118
+ renderedThumbIndexes.value = new Set<number>([0, 1, 2, 3, 4, 5, activeIndex.value])
119
+ return
120
+ }
121
+ const next = new Set<number>([0, 1, 2, 3, 4, 5, activeIndex.value])
122
+ renderedThumbIndexes.value.forEach(idx => {
123
+ if (idx >= 0 && idx < newLen) {
124
+ next.add(idx)
125
+ }
126
+ if (delta > 0 && delta <= 5 && idx + delta >= 0 && idx + delta < newLen) {
127
+ next.add(idx + delta)
128
+ }
129
+ })
130
+ if (activeIndex.value - 1 >= 0) next.add(activeIndex.value - 1)
131
+ if (activeIndex.value + 1 < newLen) next.add(activeIndex.value + 1)
132
+ renderedThumbIndexes.value = next
101
133
  })
102
134
 
103
135
  onUnmounted(() => {
@@ -744,6 +776,7 @@ const addSlideWithLayout = (layoutId: string) => {
744
776
  newSlide.subtitle = ''
745
777
  }
746
778
 
779
+ activeSlideVersion.value++
747
780
  presetPopoverVisible.value = false
748
781
  }
749
782
 
@@ -1035,6 +1068,22 @@ function getElementWidthHeight(el: any, parentGroup?: any, anchorGroup?: any): {
1035
1068
  return { width, height }
1036
1069
  }
1037
1070
 
1071
+ function getShapePatternTransform(el: any, isTriangle = false): string | undefined {
1072
+ if (!isElementPatternTile(el)) return undefined
1073
+ const dim = getElementWidthHeight(el)
1074
+ const w = Math.max(1, dim.width || 100)
1075
+ const h = Math.max(1, dim.height || 100)
1076
+ if (isTriangle) {
1077
+ return `scale(${+(100 / w).toFixed(4)}, ${+(100 / h).toFixed(4)})`
1078
+ }
1079
+ const vbW = el.viewBox ? el.viewBox[0] : 200
1080
+ const vbH = el.viewBox ? el.viewBox[1] : 200
1081
+ const bw = el.shapType === 'arc' ? 0 : (el.style?.borderWidth ? parseFloat(el.style.borderWidth) : 2)
1082
+ const effW = Math.max(1, w - bw)
1083
+ const effH = Math.max(1, h - bw)
1084
+ return `scale(${+(vbW / effW).toFixed(4)}, ${+(vbH / effH).toFixed(4)})`
1085
+ }
1086
+
1038
1087
  function getTextStrokeStyle(el: any) {
1039
1088
  if (!el.style || !el.style.enableTextStroke) return 'none'
1040
1089
  const width = el.style.textStrokeWidth || '1px'
@@ -2290,8 +2339,9 @@ const masterGroupedLayouts = computed(() => {
2290
2339
  height: '100%',
2291
2340
  backgroundColor: getElementPattern(el) ? 'transparent' : ((getElementGradientCss(el) || (getElementFill(el) !== undefined && getElementFill(el) !== 'none')) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#3b82f6')),
2292
2341
  backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
2293
- backgroundSize: getElementPattern(el) ? 'cover' : undefined,
2294
- backgroundPosition: getElementPattern(el) ? 'center center' : undefined,
2342
+ backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
2343
+ backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
2344
+ backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
2295
2345
  borderRadius: '50%',
2296
2346
  borderWidth: el.style?.borderWidth || '0px',
2297
2347
  borderStyle: el.style?.borderStyle || 'solid',
@@ -2302,8 +2352,16 @@ const masterGroupedLayouts = computed(() => {
2302
2352
  :viewBox="`0 0 ${el.viewBox ? el.viewBox[0] : (el.width || 200)} ${el.viewBox ? el.viewBox[1] : (el.height || 200)}`"
2303
2353
  preserveAspectRatio="none" style="width: 100%; height: 100%; display: block; overflow: visible;">
2304
2354
  <defs>
2305
- <pattern v-if="getElementPattern(el)" :id="`thumb-master-pat-${slide.id || index}-${el.id}`" width="100%" height="100%" patternContentUnits="objectBoundingBox">
2306
- <image :href="getElementPattern(el)" width="1" height="1" preserveAspectRatio="none" />
2355
+ <pattern v-if="getElementPattern(el)" :id="`thumb-master-pat-${slide.id || index}-${el.id}`"
2356
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : '100%'"
2357
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : '100%'"
2358
+ :patternUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
2359
+ :patternContentUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
2360
+ :patternTransform="getShapePatternTransform(el, false)">
2361
+ <image :href="getElementPattern(el)"
2362
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : 1"
2363
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : 1"
2364
+ preserveAspectRatio="none" />
2307
2365
  </pattern>
2308
2366
  <template v-if="getElementGradientConfig(el)">
2309
2367
  <radialGradient v-if="getElementGradientConfig(el)!.path && getElementGradientConfig(el)!.path !== 'line'"
@@ -2345,7 +2403,9 @@ const masterGroupedLayouts = computed(() => {
2345
2403
  <div v-else :style="{
2346
2404
  backgroundColor: getElementPattern(el) ? 'transparent' : ((getElementGradientCss(el) || (getElementFill(el) !== undefined && getElementFill(el) !== 'none')) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#3b82f6')),
2347
2405
  backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
2348
- backgroundSize: getElementPattern(el) ? 'cover' : undefined,
2406
+ backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
2407
+ backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
2408
+ backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
2349
2409
  width: '100%',
2350
2410
  height: '100%',
2351
2411
  borderRadius: el.style?.borderRadius || '0px',
@@ -2463,8 +2523,9 @@ const masterGroupedLayouts = computed(() => {
2463
2523
  height: '100%',
2464
2524
  backgroundColor: getElementPattern(el) ? 'transparent' : ((getElementGradientCss(el) || (getElementFill(el) !== undefined && getElementFill(el) !== 'none')) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#3b82f6')),
2465
2525
  backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
2466
- backgroundSize: getElementPattern(el) ? 'cover' : undefined,
2467
- backgroundPosition: getElementPattern(el) ? 'center center' : undefined,
2526
+ backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
2527
+ backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
2528
+ backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
2468
2529
  borderRadius: '50%',
2469
2530
  borderWidth: `${getElementBorderWidth(el)}px`,
2470
2531
  borderStyle: el.style?.borderStyle || (el as any).borderType || 'solid',
@@ -2478,8 +2539,16 @@ const masterGroupedLayouts = computed(() => {
2478
2539
  style="width: 100%; height: 100%; display: block; overflow: visible;"
2479
2540
  :style="{ filter: (el.shadow && el.shadow.type === 'inner') ? `url(#thumb-shape-inner-shadow-${el.id})` : '' }">
2480
2541
  <defs>
2481
- <pattern v-if="getElementPattern(el)" :id="`thumb-shape-pattern-${slide.id || index}-${el.id}`" width="100%" height="100%" patternContentUnits="objectBoundingBox">
2482
- <image :href="getElementPattern(el)" width="1" height="1" preserveAspectRatio="none" />
2542
+ <pattern v-if="getElementPattern(el)" :id="`thumb-shape-pattern-${slide.id || index}-${el.id}`"
2543
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : '100%'"
2544
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : '100%'"
2545
+ :patternUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
2546
+ :patternContentUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
2547
+ :patternTransform="getShapePatternTransform(el, true)">
2548
+ <image :href="getElementPattern(el)"
2549
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : 1"
2550
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : 1"
2551
+ preserveAspectRatio="none" />
2483
2552
  </pattern>
2484
2553
  <template v-if="getElementGradientConfig(el)">
2485
2554
  <radialGradient v-if="getElementGradientConfig(el)!.path && getElementGradientConfig(el)!.path !== 'line'"
@@ -2523,8 +2592,16 @@ const masterGroupedLayouts = computed(() => {
2523
2592
  <svg overflow="visible" style="width: 100%; height: 100%; display: block; overflow: visible;"
2524
2593
  :style="{ filter: (el.shadow && el.shadow.type === 'inner') ? `url(#thumb-shape-inner-shadow-${el.id})` : '' }">
2525
2594
  <defs>
2526
- <pattern v-if="getElementPattern(el)" :id="`thumb-shape-pattern-${slide.id || index}-${el.id}`" width="100%" height="100%" patternContentUnits="objectBoundingBox">
2527
- <image :href="getElementPattern(el)" width="1" height="1" preserveAspectRatio="none" />
2595
+ <pattern v-if="getElementPattern(el)" :id="`thumb-shape-pattern-${slide.id || index}-${el.id}`"
2596
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : '100%'"
2597
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : '100%'"
2598
+ :patternUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
2599
+ :patternContentUnits="isElementPatternTile(el) ? 'userSpaceOnUse' : 'objectBoundingBox'"
2600
+ :patternTransform="getShapePatternTransform(el, false)">
2601
+ <image :href="getElementPattern(el)"
2602
+ :width="isElementPatternTile(el) ? getElementPatternTileSize(el).w : 1"
2603
+ :height="isElementPatternTile(el) ? getElementPatternTileSize(el).h : 1"
2604
+ preserveAspectRatio="none" />
2528
2605
  </pattern>
2529
2606
  <template v-if="getElementGradientConfig(el)">
2530
2607
  <radialGradient v-if="getElementGradientConfig(el)!.path && getElementGradientConfig(el)!.path !== 'line'"
@@ -2572,8 +2649,9 @@ const masterGroupedLayouts = computed(() => {
2572
2649
  height: '100%',
2573
2650
  backgroundColor: getElementPattern(el) ? 'transparent' : ((isImagePlaceholder(el) && !getElementGradientCss(el)) ? ((getElementFill(el) && getElementFill(el) !== '#f1f5f9' && getElementFill(el) !== '#f8fafc') ? getElementFill(el) : '#d9dde3') : ((getElementGradientCss(el) || (getElementFill(el) !== undefined && getElementFill(el) !== 'none')) ? (getElementGradientCss(el) ? 'transparent' : getElementFill(el)) : (getElementFill(el) === 'none' ? 'transparent' : '#10b981'))),
2574
2651
  backgroundImage: getElementPattern(el) ? `url('${getElementPattern(el)}')` : (getElementGradientCss(el) || 'none'),
2575
- backgroundSize: getElementPattern(el) ? 'cover' : undefined,
2576
- backgroundPosition: getElementPattern(el) ? 'center center' : undefined,
2652
+ backgroundSize: getElementPattern(el) ? (isElementPatternTile(el) ? `${getElementPatternTileSize(el).w}px ${getElementPatternTileSize(el).h}px` : 'cover') : undefined,
2653
+ backgroundPosition: getElementPattern(el) ? (isElementPatternTile(el) ? '0 0' : 'center center') : undefined,
2654
+ backgroundRepeat: getElementPattern(el) ? (isElementPatternTile(el) ? 'repeat' : 'no-repeat') : undefined,
2577
2655
  borderRadius: isImagePlaceholder(el) ? ((el.style?.borderRadius && el.style.borderRadius !== '0px' && el.style.borderRadius !== '4px') ? el.style.borderRadius : '12px') : (el.style?.borderRadius !== undefined ? el.style.borderRadius : '0px'),
2578
2656
  borderWidth: (isImagePlaceholder(el) && (el.style as any)?.enableBorder !== true) ? '0px' : `${getElementBorderWidth(el)}px`,
2579
2657
  borderStyle: el.style?.borderStyle || (el as any).borderType || 'solid',
@@ -916,6 +916,9 @@ const {
916
916
  gradientDirection,
917
917
  updateElementGradient,
918
918
  shapePatternFill,
919
+ shapePatternType,
920
+ shapePatternFg,
921
+ shapePatternBg,
919
922
  handleShapePatternUpload,
920
923
  clearShapePatternFill,
921
924
  styleBorderStyle,
@@ -1709,9 +1712,9 @@ function handleUndockToPopover() {
1709
1712
  v-model:gradient-angle="gradientAngle"
1710
1713
  @update:element-gradient="updateElementGradient"
1711
1714
  v-model:shape-pattern-fill="shapePatternFill"
1712
- :shape-pattern-type="shapeFillType"
1713
- :shape-pattern-fg="fillHex"
1714
- :shape-pattern-bg="fillHex"
1715
+ v-model:shape-pattern-type="shapePatternType"
1716
+ v-model:shape-pattern-fg="shapePatternFg"
1717
+ v-model:shape-pattern-bg="shapePatternBg"
1715
1718
  :fill-opacity="fillAlpha"
1716
1719
  :is-border-enabled="enableBorder"
1717
1720
  v-model:is-border-expanded="isBorderExpanded"
@@ -1359,7 +1359,7 @@ async function handleAudioFileChange(e: Event) {
1359
1359
  </el-popover>
1360
1360
 
1361
1361
  <!-- Chart Popover -->
1362
- <el-popover trigger="click" placement="bottom-start" :width="516" popper-class="chart-popover-popper"
1362
+ <el-popover trigger="click" placement="bottom-start" :width="568" popper-class="chart-popover-popper"
1363
1363
  v-model:visible="chartPopoverVisible" :show-arrow="false" :offset="2">
1364
1364
  <template #reference>
1365
1365
  <div class="toolbar-btn-stacked" :class="{ 'btn-active': chartPopoverVisible }" title="插入图表"