@ozdao/martyrs 0.2.424 → 0.2.426

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 (37) hide show
  1. package/dist/builder.js +2 -12
  2. package/dist/builder.mjs +2 -12
  3. package/dist/martyrs/src/components/Block/Block.vue.cjs +1 -1
  4. package/dist/martyrs/src/components/Block/Block.vue.js +1 -1
  5. package/dist/martyrs/src/components/PhotoViewer/PhotoViewer.vue.cjs +87 -5
  6. package/dist/martyrs/src/components/PhotoViewer/PhotoViewer.vue.cjs.map +1 -1
  7. package/dist/martyrs/src/components/PhotoViewer/PhotoViewer.vue.js +88 -6
  8. package/dist/martyrs/src/components/PhotoViewer/PhotoViewer.vue.js.map +1 -1
  9. package/dist/martyrs/src/components/Tooltip/{Tooltip.vue.cjs → Tooltip.vue2.cjs} +2 -2
  10. package/dist/martyrs/src/components/Tooltip/Tooltip.vue2.cjs.map +1 -0
  11. package/dist/martyrs/src/components/Tooltip/{Tooltip.vue.js → Tooltip.vue2.js} +2 -2
  12. package/dist/martyrs/src/components/Tooltip/{Tooltip.vue.cjs.map → Tooltip.vue2.js.map} +1 -1
  13. package/dist/martyrs/src/modules/globals/views/components/blocks/CardHeader.vue.cjs +1 -1
  14. package/dist/martyrs/src/modules/globals/views/components/blocks/CardHeader.vue.js +1 -1
  15. package/dist/martyrs/src/modules/orders/components/blocks/CardOrderUser.vue.cjs +1 -1
  16. package/dist/martyrs/src/modules/orders/components/blocks/CardOrderUser.vue.js +1 -1
  17. package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.cjs +1 -1
  18. package/dist/martyrs/src/modules/organizations/components/pages/Members.vue.js +1 -1
  19. package/dist/martyrs/src/modules/pages/views/components/pages/PageEdit.vue.cjs +1 -1
  20. package/dist/martyrs/src/modules/pages/views/components/pages/PageEdit.vue.js +1 -1
  21. package/dist/martyrs/src/modules/products/components/blocks/ImagesThumbnails.vue.cjs +52 -14
  22. package/dist/martyrs/src/modules/products/components/blocks/ImagesThumbnails.vue.cjs.map +1 -1
  23. package/dist/martyrs/src/modules/products/components/blocks/ImagesThumbnails.vue.js +53 -15
  24. package/dist/martyrs/src/modules/products/components/blocks/ImagesThumbnails.vue.js.map +1 -1
  25. package/dist/martyrs/src/modules/products/components/pages/Product.vue.cjs +37 -65
  26. package/dist/martyrs/src/modules/products/components/pages/Product.vue.cjs.map +1 -1
  27. package/dist/martyrs/src/modules/products/components/pages/Product.vue.js +38 -66
  28. package/dist/martyrs/src/modules/products/components/pages/Product.vue.js.map +1 -1
  29. package/dist/style.css +39 -61
  30. package/package.json +1 -1
  31. package/src/builder/modes/spa.dev.js +3 -1
  32. package/src/builder/modes/spa.prod.js +1 -1
  33. package/src/builder/modes/ssr.dev.js +10 -10
  34. package/src/components/PhotoViewer/PhotoViewer.vue +140 -12
  35. package/src/modules/products/components/blocks/ImagesThumbnails.vue +46 -37
  36. package/src/modules/products/components/pages/Product.vue +5 -28
  37. package/dist/martyrs/src/components/Tooltip/Tooltip.vue.js.map +0 -1
@@ -3,47 +3,119 @@
3
3
  <img loading="lazy" :src="photoUrl" :style="imgStyle" ref="image" @load="initialize" />
4
4
  </div>
5
5
  </template>
6
-
7
6
  <script setup>
8
- import { ref, reactive, computed } from 'vue';
9
-
7
+ import { ref, reactive, computed, onMounted, watch } from 'vue';
10
8
  const props = defineProps({
11
9
  photoUrl: String,
12
10
  show: Boolean,
13
11
  });
14
-
15
12
  const scale = ref(1);
16
13
  const position = reactive({ x: 0, y: 0 });
17
14
  const dragging = ref(false);
18
15
  const startPosition = reactive({ x: 0, y: 0 });
19
16
  const startScale = ref(1);
20
17
  const startDistance = ref(0);
18
+ const image = ref(null);
19
+ const container = ref(null);
21
20
 
22
21
  const imgStyle = computed(() => ({
23
22
  transform: `scale(${scale.value}) translate(${position.x}px, ${position.y}px)`,
24
23
  }));
25
24
 
25
+ // Наблюдаем за изменением масштаба, чтобы ограничивать позицию
26
+ watch(scale, () => {
27
+ constrainPosition();
28
+ });
29
+
30
+ const getImageDimensions = () => {
31
+ if (!image.value) return { width: 0, height: 0 };
32
+
33
+ // Используем естественные размеры изображения
34
+ const imgWidth = image.value.naturalWidth;
35
+ const imgHeight = image.value.naturalHeight;
36
+
37
+ // Получаем размеры контейнера
38
+ const containerRect = image.value.parentElement?.getBoundingClientRect();
39
+ if (!containerRect) return { width: 0, height: 0 };
40
+
41
+ const containerWidth = containerRect.width;
42
+ const containerHeight = containerRect.height;
43
+
44
+ // Определяем, как изображение вписывается в контейнер (с учетом object-fit: scale-down)
45
+ let renderedWidth, renderedHeight;
46
+
47
+ if (imgWidth > containerWidth || imgHeight > containerHeight) {
48
+ const ratioX = containerWidth / imgWidth;
49
+ const ratioY = containerHeight / imgHeight;
50
+ const ratio = Math.min(ratioX, ratioY);
51
+
52
+ renderedWidth = imgWidth * ratio;
53
+ renderedHeight = imgHeight * ratio;
54
+ } else {
55
+ renderedWidth = imgWidth;
56
+ renderedHeight = imgHeight;
57
+ }
58
+
59
+ return {
60
+ renderedWidth,
61
+ renderedHeight,
62
+ containerWidth,
63
+ containerHeight
64
+ };
65
+ };
66
+
26
67
  const handleWheel = (event) => {
27
68
  event.preventDefault();
69
+
28
70
  const delta = event.deltaY > 0 ? -0.1 : 0.1;
29
- scale.value = Math.min(Math.max(1, scale.value + delta), 3);
71
+ const newScale = Math.min(Math.max(1, scale.value + delta), 3);
72
+
73
+ // Рассчитываем позицию курсора относительно центра изображения
74
+ const rect = image.value.getBoundingClientRect();
75
+ const mouseX = event.clientX - rect.left - rect.width / 2;
76
+ const mouseY = event.clientY - rect.top - rect.height / 2;
77
+
78
+ // Сохраняем соотношение позиции курсора к изображению при масштабировании
79
+ const scaleChange = newScale / scale.value;
80
+
81
+ // Обновляем позицию так, чтобы точка под курсором оставалась на месте
82
+ position.x = position.x - (mouseX / scale.value) * (scaleChange - 1);
83
+ position.y = position.y - (mouseY / scale.value) * (scaleChange - 1);
84
+
85
+ // Обновляем масштаб
86
+ scale.value = newScale;
87
+
88
+ // Ограничиваем позицию после обновления масштаба
89
+ constrainPosition();
30
90
  };
31
91
 
32
92
  const startDrag = (event) => {
33
93
  event.preventDefault();
94
+ if (scale.value <= 1) return; // Не позволяем перетаскивать при масштабе 1
95
+
34
96
  dragging.value = true;
35
97
  startPosition.x = event.type === 'mousedown' ? event.clientX : event.touches[0].clientX;
36
98
  startPosition.y = event.type === 'mousedown' ? event.clientY : event.touches[0].clientY;
99
+
37
100
  const move = (moveEvent) => {
38
101
  if (dragging.value) {
39
102
  const currentX = moveEvent.type === 'mousemove' ? moveEvent.clientX : moveEvent.touches[0].clientX;
40
103
  const currentY = moveEvent.type === 'mousemove' ? moveEvent.clientY : moveEvent.touches[0].clientY;
41
- position.x += (currentX - startPosition.x) / scale.value;
42
- position.y += (currentY - startPosition.y) / scale.value;
104
+
105
+ const deltaX = (currentX - startPosition.x) / scale.value;
106
+ const deltaY = (currentY - startPosition.y) / scale.value;
107
+
108
+ position.x += deltaX;
109
+ position.y += deltaY;
110
+
111
+ // Ограничиваем позицию после перемещения
112
+ constrainPosition();
113
+
43
114
  startPosition.x = currentX;
44
115
  startPosition.y = currentY;
45
116
  }
46
117
  };
118
+
47
119
  const endDrag = () => {
48
120
  dragging.value = false;
49
121
  document.removeEventListener('mousemove', move);
@@ -51,6 +123,7 @@ const startDrag = (event) => {
51
123
  document.removeEventListener('touchmove', move);
52
124
  document.removeEventListener('touchend', endDrag);
53
125
  };
126
+
54
127
  document.addEventListener('mousemove', move);
55
128
  document.addEventListener('mouseup', endDrag);
56
129
  document.addEventListener('touchmove', move);
@@ -79,7 +152,25 @@ const handleTouchMove = (event) => {
79
152
  const distanceY = Math.abs(touch1.clientY - touch2.clientY);
80
153
  const newDistance = Math.sqrt(distanceX ** 2 + distanceY ** 2);
81
154
  const scaleFactor = newDistance / startDistance.value;
82
- scale.value = Math.min(Math.max(1, startScale.value * scaleFactor), 3);
155
+
156
+ // Получаем центр между двумя касаниями
157
+ const centerX = (touch1.clientX + touch2.clientX) / 2;
158
+ const centerY = (touch1.clientY + touch2.clientY) / 2;
159
+
160
+ // Получаем координаты центра касания относительно изображения
161
+ const rect = image.value.getBoundingClientRect();
162
+ const touchCenterX = centerX - rect.left - rect.width / 2;
163
+ const touchCenterY = centerY - rect.top - rect.height / 2;
164
+
165
+ const newScale = Math.min(Math.max(1, startScale.value * scaleFactor), 3);
166
+ const scaleChange = newScale / scale.value;
167
+
168
+ // Обновляем позицию, чтобы центр касания оставался на месте
169
+ position.x = position.x - (touchCenterX / scale.value) * (scaleChange - 1);
170
+ position.y = position.y - (touchCenterY / scale.value) * (scaleChange - 1);
171
+
172
+ scale.value = newScale;
173
+ constrainPosition();
83
174
  }
84
175
  };
85
176
 
@@ -94,8 +185,45 @@ const initialize = () => {
94
185
  position.x = 0;
95
186
  position.y = 0;
96
187
  };
97
- </script>
98
188
 
189
+ const constrainPosition = () => {
190
+ // Если масштаб 1, сбрасываем позицию в центр
191
+ if (scale.value <= 1) {
192
+ position.x = 0;
193
+ position.y = 0;
194
+ return;
195
+ }
196
+
197
+ const { renderedWidth, renderedHeight, containerWidth, containerHeight } = getImageDimensions();
198
+
199
+ // Вычисляем максимальное смещение
200
+ const scaledWidth = renderedWidth * scale.value;
201
+ const scaledHeight = renderedHeight * scale.value;
202
+
203
+ // Отступ от края изображения до края контейнера при текущем масштабе
204
+ const horizontalOffset = (scaledWidth - containerWidth) / 2 / scale.value;
205
+ const verticalOffset = (scaledHeight - containerHeight) / 2 / scale.value;
206
+
207
+ // Ограничиваем смещение, чтобы изображение не выходило за пределы контейнера
208
+ if (horizontalOffset > 0) {
209
+ position.x = Math.max(-horizontalOffset, Math.min(horizontalOffset, position.x));
210
+ } else {
211
+ position.x = 0;
212
+ }
213
+
214
+ if (verticalOffset > 0) {
215
+ position.y = Math.max(-verticalOffset, Math.min(verticalOffset, position.y));
216
+ } else {
217
+ position.y = 0;
218
+ }
219
+ };
220
+
221
+ onMounted(() => {
222
+ window.addEventListener('resize', () => {
223
+ constrainPosition();
224
+ });
225
+ });
226
+ </script>
99
227
  <style>
100
228
  .photo-container {
101
229
  width: 100%;
@@ -105,11 +233,11 @@ const initialize = () => {
105
233
  position: relative;
106
234
  cursor: grab;
107
235
  }
108
-
109
236
  .photo-container img {
110
237
  width: 100%;
111
238
  height: 100%;
112
239
  object-fit: scale-down;
113
- transition: transform 0.3s;
240
+ transition: transform 0.05s;
241
+ transform-origin: center center;
114
242
  }
115
- </style>
243
+ </style>
@@ -1,14 +1,41 @@
1
1
  <template>
2
- <div class="o-scroll w-100 product-images">
3
- <div class="no-responsive w-max flex flex-nowrap gap-thin">
4
- <img loading="lazy"
5
- v-for="(image, index) in images"
6
- :key="index"
7
- :src="(FILE_SERVER_URL || '') + image"
8
- @click="openPopup(image)"
9
- class="flex-child flex-child-grow-1 aspect-1x1 radius-semi bg-white o-hidden thumbnail"
2
+ <div class="w-100 o-hidden bg-light radius-big flex-nowrap flex-column flex pos-relative">
3
+ <!-- Main image container -->
4
+ <div class="pd-semi w-100 bg-light radius-semi o-hidden" @click="openPopup(images[0])">
5
+ <Images360
6
+ v-if="product && product.image3d"
7
+ class="h-100 w-100"
8
+ :imagePath="`/assets/images/products/${product.image3d}`"
9
+ :imageCount="36"
10
10
  />
11
+ <img
12
+ loading="lazy"
13
+ v-if="images[0] && !product?.image3d"
14
+ class="h-max h-max-15r bg-white radius-semi w-100"
15
+ style="object-fit: contain;"
16
+ :src="(FILE_SERVER_URL || '') + images[0]"
17
+ />
18
+ <PlaceholderImage
19
+ v-if="!images[0] && !product?.image3d"
20
+ class="h-max-20r h-100 w-100"
21
+ style="object-fit: cover;"
22
+ />
23
+ </div>
24
+
25
+ <!-- Thumbnails -->
26
+ <div v-if="images.length > 1" class="o-scroll w-100 pd-semi pd-t-zero">
27
+ <div class="no-responsive w-max flex flex-nowrap gap-thin">
28
+ <img loading="lazy"
29
+ v-for="(image, index) in images"
30
+ :key="index"
31
+ :src="(FILE_SERVER_URL || '') + image"
32
+ @click="openPopup(image)"
33
+ class="flex-child flex-child-grow-1 aspect-1x1 radius-semi bg-white o-hidden thumbnail"
34
+ />
35
+ </div>
11
36
  </div>
37
+
38
+ <!-- Popup with photo viewer -->
12
39
  <Popup @close-popup="closePopup" :isPopupOpen="isPopupVisible" class="radius-medium o-hidden">
13
40
  <PhotoViewer
14
41
  :photoUrl="(FILE_SERVER_URL || '') + selectedImage"
@@ -20,20 +47,28 @@
20
47
  <script setup>
21
48
  import { ref } from "vue";
22
49
 
23
- import Popup from '@martyrs/src/components/Popup/Popup.vue'
24
- import PhotoViewer from '@martyrs/src/components/PhotoViewer/PhotoViewer.vue'
50
+ import Popup from '@martyrs/src/components/Popup/Popup.vue';
51
+ import PhotoViewer from '@martyrs/src/components/PhotoViewer/PhotoViewer.vue';
52
+
53
+
54
+ import Images360 from '@martyrs/src/modules/products/components/blocks/Images360.vue'
25
55
 
26
56
  const props = defineProps({
27
57
  images: {
28
58
  type: Array,
29
59
  required: true,
30
- }
60
+ },
61
+ product: {
62
+ type: Object,
63
+ default: () => ({})
64
+ },
31
65
  });
32
66
 
33
67
  const isPopupVisible = ref(false);
34
68
  const selectedImage = ref(null);
35
69
 
36
70
  const openPopup = (image) => {
71
+ if (!image) return;
37
72
  selectedImage.value = image;
38
73
  isPopupVisible.value = true;
39
74
  };
@@ -44,14 +79,6 @@ const closePopup = () => {
44
79
  </script>
45
80
 
46
81
  <style scoped>
47
- .magnifier {
48
- max-width: 32rem;
49
- }
50
- .product-images {
51
- display: flex;
52
- flex-direction: column;
53
- }
54
-
55
82
  .thumbnail {
56
83
  width: 100%;
57
84
  max-width: 6rem;
@@ -59,22 +86,4 @@ const closePopup = () => {
59
86
  object-fit: cover;
60
87
  cursor: pointer;
61
88
  }
62
-
63
- .popup {
64
- position: fixed;
65
- top: 0;
66
- left: 0;
67
- width: 100vw;
68
- height: 100vh;
69
- background-color: rgba(0, 0, 0, 0.8);
70
- display: flex;
71
- justify-content: center;
72
- align-items: center;
73
- cursor: zoom-out;
74
- }
75
-
76
- .popup-image {
77
- max-width: 90%;
78
- max-height: 90%;
79
- }
80
89
  </style>
@@ -1,33 +1,11 @@
1
1
  <template>
2
2
  <div v-if="products.state.current._id" class="h-100 w-100 mobile:pd-thin pd-big bg-white">
3
3
  <div class="cols-2-1_2 w-100 gap-medium">
4
- <div class="w-100 o-hidden bg-light radius-big flex-nowrap flex-column flex pos-relative">
5
- <div class="pd-semi w-100 bg-light radius-semi o-hidden">
6
- <Images360
7
- v-if="product && product.image3d"
8
- class="h-100 w-100"
9
- :imagePath="`/assets/images/products/${product.image3d}`"
10
- :imageCount="36"
11
- />
12
- <img
13
- loading="lazy"
14
- v-if="images[0] && !product.image3d"
15
- class="h-max h-max-15r bg-white radius-semi w-100"
16
- style="object-fit: contain;"
17
- :src="(FILE_SERVER_URL || '') + images[0]"
18
- />
19
- <PlaceholderImage
20
- v-if="!images[0] && !product.image3d"
21
- class="h-max-20r h-100 w-100"
22
- style="object-fit: cover;"
23
- />
24
- </div>
25
- <ImagesThumbnails
26
- v-if="images.length > 1"
27
- :images="images"
28
- class="pd-semi pd-t-zero"
29
- />
30
- </div>
4
+
5
+ <ImagesThumbnails
6
+ :images="images"
7
+ :product="product"
8
+ />
31
9
 
32
10
  <div class="pos-relative w-100 h-100 flex-column flex-h-center flex">
33
11
  <IconEdit
@@ -138,7 +116,6 @@ import PopupDateSelector from '@martyrs/src/modules/globals/views/components/blo
138
116
  import IconEdit from '@martyrs/src/modules/icons/navigation/IconEdit.vue'
139
117
  import IconShopcartAdd from '@martyrs/src/modules/icons/actions/IconShopcartAdd.vue'
140
118
  import PlaceholderImage from '@martyrs/src/modules/icons/placeholders/PlaceholderImage.vue'
141
- import Images360 from '@martyrs/src/modules/products/components/blocks/Images360.vue'
142
119
  import ImagesThumbnails from '@martyrs/src/modules/products/components/blocks/ImagesThumbnails.vue'
143
120
  import Price from '@martyrs/src/modules/products/components/elements/Price.vue'
144
121
  import PopularProducts from '@martyrs/src/modules/products/components/sections/PopularProducts.vue'
@@ -1 +0,0 @@
1
- {"version":3,"file":"Tooltip.vue.js","sources":["../../../../../src/components/Tooltip/Tooltip.vue"],"sourcesContent":["<template>\n <div class=\"cursor-pointer tooltip-container\" @mouseover=\"showTooltip\" @mouseleave=\"hideTooltip\">\n <slot></slot>\n <div class=\"tooltip-content\" :style=\"tooltipStyle\">\n {{ text }}\n </div>\n </div>\n</template>\n\n<script setup>\nimport { ref, reactive } from 'vue';\n\nconst props = defineProps({\n text: {\n type: String,\n required: true\n }\n});\n\nconst visible = ref(false);\n\nconst tooltipStyle = reactive({\n position: 'absolute',\n width: 'max-content',\n zIndex: 1000,\n background: '#333',\n color: '#fff',\n padding: '5px 10px',\n borderRadius: '3px',\n fontSize: '14px',\n display: 'none',\n});\n\nfunction showTooltip(event) {\n visible.value = true;\n tooltipStyle.left = `${(event.clientX / 100) + 10 }px`;\n tooltipStyle.top = `${(event.clientY / 100) + 10 }px`;\n tooltipStyle.display = 'block';\n}\n\nfunction hideTooltip() {\n visible.value = false;\n tooltipStyle.display = 'none';\n}\n</script>\n\n<style >\n.tooltip-container {\n position: relative;\n display: inline-block;\n}\n\n.tooltip-content {\n pointer-events: none;\n}\n</style>"],"names":[],"mappings":";;;;;;;;;;;AAmBA,UAAM,UAAU,IAAI,KAAK;AAEzB,UAAM,eAAe,SAAS;AAAA,MAC5B,UAAU;AAAA,MACV,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,OAAO;AAAA,MACP,SAAS;AAAA,MACT,cAAc;AAAA,MACd,UAAU;AAAA,MACV,SAAS;AAAA,IACX,CAAC;AAED,aAAS,YAAY,OAAO;AAC1B,cAAQ,QAAQ;AAChB,mBAAa,OAAO,GAAI,MAAM,UAAU,MAAO,EAAE;AACjD,mBAAa,MAAM,GAAI,MAAM,UAAU,MAAO,EAAE;AAChD,mBAAa,UAAU;AAAA,IACzB;AAEA,aAAS,cAAc;AACrB,cAAQ,QAAQ;AAChB,mBAAa,UAAU;AAAA,IACzB;;;;;;;;;;;;;;;;"}