Skip to content

Commit 50f9cf0

Browse files
Merge pull request #118 from jfrog/MAR-10487
MAR-10489 - Research img open preview
2 parents cb13784 + 1354f44 commit 50f9cf0

File tree

2 files changed

+153
-11
lines changed

2 files changed

+153
-11
lines changed

src/components/ImageModal.vue

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<template>
2+
<div v-if="visible" class="modal-overlay" @click.self="$emit('close')">
3+
<div class="modal-content">
4+
<button class="modal-close-button" @click="$emit('close')">
5+
&times;
6+
</button>
7+
<img :srcset="imageUrl" alt="Full Preview" class="modal-image"/>
8+
</div>
9+
</div>
10+
</template>
11+
12+
<script>
13+
// No imports needed for standard Options API features
14+
15+
export default {
16+
// 1. Define component name
17+
name: 'ImageModal',
18+
19+
// 2. Define props
20+
props: {
21+
visible: {
22+
type: Boolean,
23+
required: true
24+
},
25+
imageUrl: {
26+
type: String,
27+
default: ''
28+
}
29+
},
30+
31+
// 3. Define emitted events (Optional, but good practice in Vue 2)
32+
emits: ['close'],
33+
34+
};
35+
</script>
36+
37+
<style scoped>
38+
.modal-overlay {
39+
position: fixed;
40+
top: 0;
41+
left: 0;
42+
width: 100%;
43+
height: 100%;
44+
background-color: rgba(0, 0, 0, 0.9); /* Dark, semi-transparent background */
45+
display: flex;
46+
justify-content: center;
47+
align-items: center;
48+
z-index: 9999; /* Ensure it's on top of everything */
49+
}
50+
51+
.modal-content {
52+
position: relative;
53+
max-width: 90%;
54+
max-height: 90%;
55+
}
56+
57+
.modal-image {
58+
max-width: 100%;
59+
max-height: 80vh; /* Viewport height for a better fit */
60+
display: block;
61+
}
62+
63+
.modal-close-button {
64+
position: absolute;
65+
top: 0px;
66+
right: 0px;
67+
background: rgba(255, 255, 255, 0.2);
68+
color: white;
69+
border: none;
70+
border-radius: 50%;
71+
font-size: 1.5rem;
72+
padding: 0 1rem;
73+
cursor: pointer;
74+
z-index: 10000;
75+
width: 30px;
76+
height: 30px;
77+
display: flex
78+
;
79+
justify-content: center;
80+
align-items: center;
81+
}
82+
</style>

src/components/post/RealTimePostContent.vue

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,99 @@
11
<template>
2-
<section
3-
v-html="content"
4-
ref="codeSection"
5-
class="latest-posts-single-post-content text-black"
6-
/>
2+
<div class="post-content-wrapper">
3+
<section
4+
v-html="content"
5+
ref="codeSection"
6+
class="latest-posts-single-post-content text-black"
7+
/>
8+
9+
<ImageModal
10+
:visible="isModalVisible"
11+
:image-url="selectedImageUrl"
12+
@close="closeModal"
13+
/>
14+
</div>
715
</template>
816

917
<script>
10-
import hljs from 'highlight.js'; // Import Highlight.js
11-
import 'highlight.js/styles/atom-one-dark-reasonable.css'; // Import the desired style
18+
import hljs from 'highlight.js';
19+
import 'highlight.js/styles/atom-one-dark-reasonable.css';
20+
import ImageModal from '../ImageModal.vue';
1221
1322
export default {
1423
name: 'PostContent',
24+
components: {
25+
ImageModal, // Register the modal component
26+
},
1527
props: {
1628
content: {
1729
type: String,
1830
default: 'lorem ipsum'
1931
},
2032
},
33+
data() {
34+
return {
35+
isModalVisible: false,
36+
selectedImageUrl: '',
37+
};
38+
},
2139
methods: {
40+
// Existing method for code highlighting
2241
highlightCode() {
42+
// It's important to check if the ref exists before querying the DOM
43+
if (!this.$refs.codeSection) return;
2344
24-
// Target only the code blocks in the section
2545
const codeBlocks = this.$refs.codeSection.querySelectorAll('pre code');
2646
codeBlocks.forEach((block) => {
27-
hljs.highlightBlock(block); // Highlight the code block
47+
hljs.highlightBlock(block);
2848
});
2949
},
50+
51+
// Attach click listeners to images
52+
attachImageClickEvent() {
53+
// It's important to check if the ref exists before querying the DOM
54+
if (!this.$refs.codeSection) return;
55+
56+
const images = this.$refs.codeSection.querySelectorAll('img');
57+
58+
images.forEach(img => {
59+
// Prevent attaching the listener multiple times on component updates
60+
if (img.dataset.clickAttached) return;
61+
62+
img.style.cursor = 'zoom-in'; // Visual cue for the user
63+
64+
// 1. Define the handler function
65+
const clickHandler = (event) => {
66+
event.preventDefault(); // Prevent default link behavior if the image is wrapped in an <a> tag
67+
this.openModal(img.src);
68+
};
69+
70+
// 2. Attach the native DOM event listener
71+
img.addEventListener('click', clickHandler);
72+
73+
// 3. Mark the image as handled
74+
img.dataset.clickAttached = 'true';
75+
});
76+
},
77+
78+
// 🆕 NEW METHOD: Modal controls
79+
openModal(url) {
80+
this.selectedImageUrl = url;
81+
this.isModalVisible = true;
82+
},
83+
closeModal() {
84+
this.isModalVisible = false;
85+
this.selectedImageUrl = '';
86+
},
3087
},
31-
mounted() {
3288
33-
// Apply syntax highlighting when mounted
89+
mounted() {
90+
// Apply syntax highlighting
3491
this.highlightCode();
92+
// 🆕 Attach image click event after initial render
93+
this.attachImageClickEvent();
3594
},
3695
96+
3797
}
3898
</script>
3999

0 commit comments

Comments
 (0)