Skip to content

Commit bb23d6e

Browse files
author
Sebi Nemeth
committed
beautify
1 parent 0602aef commit bb23d6e

File tree

7 files changed

+135
-15
lines changed

7 files changed

+135
-15
lines changed

dist/index.js

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,19 @@ const css = `
5959
background: #00c0ff !important;
6060
box-shadow: 0px 0px 5px #00c0ff !important;
6161
}
62+
.live-translator-box {
63+
outline: solid 2px green;
64+
background: green;
65+
opacity: 0.1;
66+
position: absolute;
67+
border-radius: 4px;
68+
z-index: 9999;
69+
display: none;
70+
}
71+
.live-translator-box.attribute {
72+
outline: solid 2px blue;
73+
background: blue;
74+
}
6275
`;
6376
function deepForIn(object, fn) {
6477
const iteratee = (v, k) => {
@@ -143,6 +156,7 @@ class LiveTranslatorManager {
143156
_options;
144157
_enableButton;
145158
_indicator;
159+
_box;
146160
constructor(options) {
147161
this._enabled = false;
148162
this._options = options;
@@ -170,6 +184,9 @@ class LiveTranslatorManager {
170184
this.render();
171185
});
172186
document.body.appendChild(this._enableButton);
187+
this._box = document.createElement('div');
188+
this._box.classList.add('live-translator-box');
189+
document.body.appendChild(this._box);
173190
// initialize encode
174191
// encode is moved to i18n.ts file
175192
// initialize decode & render
@@ -182,6 +199,7 @@ class LiveTranslatorManager {
182199
childList: false,
183200
});
184201
document.documentElement.addEventListener('mousemove', throttler);
202+
window.setInterval(throttler, 1000);
185203
// render for the first time
186204
this.render();
187205
}
@@ -199,6 +217,7 @@ class LiveTranslatorManager {
199217
}
200218
render() {
201219
const badgeWrappers = document.querySelectorAll('.live-translator-badge-wrapper');
220+
this._box.style.display = 'none';
202221
badgeWrappers.forEach((wrapper) => {
203222
wrapper.remove();
204223
});
@@ -212,11 +231,14 @@ class LiveTranslatorManager {
212231
const node = queue.pop();
213232
const badges = [];
214233
const parent = node.parentElement;
234+
const rect = getBoundingClientRect(node);
215235
if (node instanceof Text) {
216236
const matches = node.textContent.match(re);
217237
for (const match of matches ?? []) {
218238
const meta = JSON.parse(ZeroWidthEncoder.decode(match));
219-
badges.push(createBadge(meta, this._options));
239+
const badge = createBadge(meta, this._options, node);
240+
badge.addEventListener('mouseenter', () => this.showBox(node));
241+
badges.push(badge);
220242
}
221243
}
222244
const attributes = (node.attributes ? [...node.attributes] : [])
@@ -225,7 +247,9 @@ class LiveTranslatorManager {
225247
for (const { attribute, match } of attributes) {
226248
for (const m of match) {
227249
const meta = JSON.parse(ZeroWidthEncoder.decode(m));
228-
badges.push(createBadge(meta, this._options, attribute.name));
250+
const badge = createBadge(meta, this._options, node, attribute.name);
251+
badge.addEventListener('mouseenter', () => this.showBox(node, true));
252+
badges.push(badge);
229253
}
230254
}
231255
if (badges.length) {
@@ -234,8 +258,11 @@ class LiveTranslatorManager {
234258
container = node.previousElementSibling;
235259
}
236260
else {
261+
const parentRect = getBoundingClientRect(node instanceof Text ? parent : node);
237262
container = document.createElement('span');
238263
container.classList.add('live-translator-badge-container');
264+
container.style.top = rect.top - parentRect.top + 'px';
265+
container.style.left = rect.left - parentRect.left + 'px';
239266
const relativeWrapper = document.createElement('span');
240267
relativeWrapper.classList.add('live-translator-badge-wrapper');
241268
relativeWrapper.appendChild(container);
@@ -250,8 +277,26 @@ class LiveTranslatorManager {
250277
}
251278
}
252279
}
280+
showBox(node, attribute = false) {
281+
const rect = !attribute ? getBoundingClientRect(node) : node.getClientRects()[0];
282+
if (!rect) {
283+
return;
284+
}
285+
if (attribute) {
286+
this._box.classList.add('attribute');
287+
}
288+
else {
289+
this._box.classList.remove('attribute');
290+
}
291+
const padding = 2;
292+
this._box.style.top = rect.top - padding + window.scrollY + 'px';
293+
this._box.style.left = rect.left - padding + window.scrollX + 'px';
294+
this._box.style.width = rect.width + 2 * padding + 'px';
295+
this._box.style.height = rect.height + 2 * padding + 'px';
296+
this._box.style.display = 'block';
297+
}
253298
}
254-
const createBadge = (meta, options, attribute) => {
299+
const createBadge = (meta, options, node, attribute) => {
255300
const badge = document.createElement('a');
256301
badge.classList.add('live-translator-badge');
257302
let title = meta.path + ': ' + meta.message;
@@ -272,6 +317,11 @@ const createBadge = (meta, options, attribute) => {
272317
});
273318
return badge;
274319
};
320+
function getBoundingClientRect(node, textOffset) {
321+
const range = document.createRange();
322+
range.selectNodeContents(node);
323+
return range.getBoundingClientRect();
324+
}
275325
export const LiveTranslatorPlugin = {
276326
install(app, options) {
277327
console.log('LiveTranslator is installed');

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vue-i18n-live-translator-plugin",
3-
"version": "1.1.0",
3+
"version": "1.1.1",
44
"description": "",
55
"main": "./dist/index.js",
66
"types": "./dist/index.d.ts",

src/demo/App.vue

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,13 @@
3737
{{ $t('LTPlugin.MessagesCount', pluralCount) }}
3838
</p>
3939
<h3>Multiple strings inside one tag</h3>
40-
<p class="translated">
40+
<p class="translated" :title="t('LTPlugin.MultipleTitle')">
4141
{{ t('LTPlugin.PartOne') }} {{ $t('LTPlugin.PartTwo') }}
4242
</p>
43+
<h3>Scrollable container</h3>
44+
<div class="scroll">
45+
<div class="item" v-for="i in 5">{{ t('LTPlugin.ListItemN', [i]) }}</div>
46+
</div>
4347
<h3>Attribute</h3>
4448
<img class="image" src="https://source.unsplash.com/random/500x500" :alt="t('LTPlugin.Attrs.ImageAlt')"
4549
:title="t('LTPlugin.Attrs.ImageTitle')">
@@ -103,6 +107,15 @@ const showMeta = computed(() => {
103107
width: 2rem;
104108
}
105109
110+
.scroll {
111+
height: 200px;
112+
overflow-y: scroll;
113+
box-shadow: inset 0px 0px 4px rgba(0,0,0,0.5);
114+
.item {
115+
line-height: 50px;
116+
}
117+
}
118+
106119
.image {
107120
width: 80%;
108121
border-radius: 8px;

src/demo/lang/en.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
"ImageTitle": "Image title"
1010
},
1111
"PartOne": "Part one",
12-
"PartTwo": "Part two"
12+
"PartTwo": "Part two",
13+
"ListItemN": "Item {0}",
14+
"MultipleTitle": "Multiple strings inside one tag"
1315
}
1416
}

src/demo/lang/hu.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
"ImageTitle": "Képcím"
1010
},
1111
"PartOne": "Első rész",
12-
"PartTwo": "Második rész"
12+
"PartTwo": "Második rész",
13+
"ListItemN": "Elem {0}",
14+
"MultipleTitle": "Több string egy tagen belül"
1315
}
1416
}

src/index.ts

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,19 @@ const css = `
6060
background: #00c0ff !important;
6161
box-shadow: 0px 0px 5px #00c0ff !important;
6262
}
63+
.live-translator-box {
64+
outline: solid 2px green;
65+
background: green;
66+
opacity: 0.1;
67+
position: absolute;
68+
border-radius: 4px;
69+
z-index: 9999;
70+
display: none;
71+
}
72+
.live-translator-box.attribute {
73+
outline: solid 2px blue;
74+
background: blue;
75+
}
6376
`
6477
export type TranslationMeta = {
6578
locale: string,
@@ -165,6 +178,8 @@ class LiveTranslatorManager {
165178
_enableButton: HTMLButtonElement
166179
_indicator: HTMLSpanElement
167180

181+
_box: HTMLDivElement
182+
168183
constructor (options: LiveTranslatorPluginOptions) {
169184
this._enabled = false
170185
this._options = options
@@ -197,6 +212,10 @@ class LiveTranslatorManager {
197212
})
198213
document.body.appendChild(this._enableButton)
199214

215+
this._box = document.createElement('div')
216+
this._box.classList.add('live-translator-box')
217+
document.body.appendChild(this._box)
218+
200219
// initialize encode
201220
// encode is moved to i18n.ts file
202221

@@ -212,6 +231,7 @@ class LiveTranslatorManager {
212231
},
213232
)
214233
document.documentElement.addEventListener('mousemove', throttler)
234+
window.setInterval(throttler, 1000)
215235

216236
// render for the first time
217237
this.render()
@@ -231,6 +251,7 @@ class LiveTranslatorManager {
231251

232252
render () {
233253
const badgeWrappers = document.querySelectorAll('.live-translator-badge-wrapper')
254+
this._box.style.display = 'none'
234255
badgeWrappers.forEach((wrapper) => {
235256
wrapper.remove()
236257
})
@@ -248,13 +269,16 @@ class LiveTranslatorManager {
248269
const node = queue.pop() as HTMLElement
249270

250271
const badges = [] as HTMLElement[]
251-
const parent = node.parentElement as Element
272+
const parent = node.parentElement as HTMLElement
252273

274+
const rect = getBoundingClientRect(node)
253275
if (node instanceof Text) {
254276
const matches = (node.textContent as string).match(re)
255277
for (const match of matches ?? []) {
256278
const meta = JSON.parse(ZeroWidthEncoder.decode(match)) as TranslationMeta
257-
badges.push(createBadge(meta, this._options))
279+
const badge = createBadge(meta, this._options, node)
280+
badge.addEventListener('mouseenter', () => this.showBox(node))
281+
badges.push(badge)
258282
}
259283
}
260284

@@ -264,17 +288,22 @@ class LiveTranslatorManager {
264288
for (const { attribute, match } of attributes) {
265289
for (const m of (match as RegExpMatchArray)) {
266290
const meta = JSON.parse(ZeroWidthEncoder.decode(m)) as TranslationMeta
267-
badges.push(createBadge(meta, this._options, attribute.name))
291+
const badge = createBadge(meta, this._options, node, attribute.name)
292+
badge.addEventListener('mouseenter', () => this.showBox(node, true))
293+
badges.push(badge)
268294
}
269295
}
270296

271297
if (badges.length) {
272-
let container: Element
298+
let container: HTMLElement
273299
if (node.previousElementSibling && node.previousElementSibling.classList.contains('live-translator-badge-container')) {
274-
container = node.previousElementSibling
300+
container = node.previousElementSibling as HTMLElement
275301
} else {
302+
const parentRect = getBoundingClientRect(node instanceof Text ? parent : node);
276303
container = document.createElement('span')
277304
container.classList.add('live-translator-badge-container')
305+
container.style.top = rect.top - parentRect.top + 'px'
306+
container.style.left = rect.left - parentRect.left + 'px'
278307
const relativeWrapper = document.createElement('span')
279308
relativeWrapper.classList.add('live-translator-badge-wrapper')
280309
relativeWrapper.appendChild(container)
@@ -290,9 +319,27 @@ class LiveTranslatorManager {
290319
}
291320
}
292321
}
322+
323+
showBox(node: Node, attribute = false) {
324+
const rect = !attribute ? getBoundingClientRect(node) : (node as Element).getClientRects()[0]
325+
if (!rect) {
326+
return
327+
}
328+
if (attribute) {
329+
this._box.classList.add('attribute')
330+
} else {
331+
this._box.classList.remove('attribute')
332+
}
333+
const padding = 2
334+
this._box.style.top = rect.top - padding + window.scrollY + 'px'
335+
this._box.style.left = rect.left - padding + window.scrollX + 'px'
336+
this._box.style.width = rect.width + 2 * padding + 'px'
337+
this._box.style.height = rect.height + 2 * padding + 'px'
338+
this._box.style.display = 'block'
339+
}
293340
}
294341

295-
const createBadge = (meta: TranslationMeta, options: LiveTranslatorPluginOptions, attribute?: string) => {
342+
const createBadge = (meta: TranslationMeta, options: LiveTranslatorPluginOptions, node: Node, attribute?: string) => {
296343
const badge = document.createElement('a')
297344
badge.classList.add('live-translator-badge')
298345
let title = meta.path + ': ' + meta.message
@@ -313,6 +360,12 @@ const createBadge = (meta: TranslationMeta, options: LiveTranslatorPluginOptions
313360
return badge
314361
}
315362

363+
function getBoundingClientRect(node: Node, textOffset?: number) {
364+
const range = document.createRange();
365+
range.selectNodeContents(node);
366+
return range.getBoundingClientRect()
367+
}
368+
316369
export const LiveTranslatorPlugin = {
317370
install (app: any, options: LiveTranslatorPluginOptions) {
318371
console.log('LiveTranslator is installed')

0 commit comments

Comments
 (0)