@finema/finework-layer 2.0.63 → 2.0.65
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.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.0.65](https://gitlab.finema.co/finema/internal/finework/compare/2.0.64...2.0.65) (2026-09-15)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* add resizable panel to manual viewer with improved PDF navigation ([dc37e28](https://gitlab.finema.co/finema/internal/finework/commit/dc37e28790a2e16168ed1e1264d621efb17cfe56))
|
|
8
|
+
|
|
9
|
+
## [2.0.64](https://gitlab.finema.co/finema/internal/finework/compare/2.0.63...2.0.64) (2026-09-15)
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* fetch PDF as blob to bypass X-Frame-Options restrictions and add loading states ([4584b67](https://gitlab.finema.co/finema/internal/finework/commit/4584b67b555b19a3d2f10a25f1d89f9340dec134))
|
|
14
|
+
|
|
3
15
|
## [2.0.63](https://gitlab.finema.co/finema/internal/finework/compare/2.0.62...2.0.63) (2026-09-15)
|
|
4
16
|
|
|
5
17
|
## [2.0.62](https://gitlab.finema.co/finema/internal/finework/compare/2.0.61...2.0.62) (2026-09-15)
|
|
@@ -4,12 +4,22 @@
|
|
|
4
4
|
:portal="true"
|
|
5
5
|
:modal="false"
|
|
6
6
|
:dismissible="false"
|
|
7
|
-
:ui="{ content: 'w-full
|
|
7
|
+
:ui="{ content: 'w-full' }"
|
|
8
|
+
:content="{
|
|
9
|
+
style: {
|
|
10
|
+
width: `${panelWidth}px`,
|
|
11
|
+
maxWidth: '100vw',
|
|
12
|
+
},
|
|
13
|
+
}"
|
|
8
14
|
side="right"
|
|
9
15
|
@update:open="(val) => emit('update', val)"
|
|
10
16
|
>
|
|
11
17
|
<template #content>
|
|
12
|
-
<div class="flex h-full flex-col">
|
|
18
|
+
<div class="relative flex h-full flex-col">
|
|
19
|
+
<div
|
|
20
|
+
class="hover:bg-primary-300/60 active:bg-primary-400/70 absolute inset-y-0 left-0 z-10 w-1.5 -translate-x-1/2 cursor-col-resize touch-none"
|
|
21
|
+
@mousedown="startResize"
|
|
22
|
+
/>
|
|
13
23
|
<!-- Header -->
|
|
14
24
|
<div class="flex items-center justify-between border-b border-gray-200 px-5 py-4">
|
|
15
25
|
<div class="flex min-w-0 items-center gap-3">
|
|
@@ -246,9 +256,9 @@
|
|
|
246
256
|
class="relative flex-1 overflow-hidden bg-gray-600"
|
|
247
257
|
>
|
|
248
258
|
<iframe
|
|
249
|
-
v-if="
|
|
250
|
-
:key="
|
|
251
|
-
:src="`${
|
|
259
|
+
v-if="viewerBlobUrl"
|
|
260
|
+
:key="viewerBlobUrl"
|
|
261
|
+
:src="`${viewerBlobUrl}#view=FitH&navpanes=0`"
|
|
252
262
|
title="User manual"
|
|
253
263
|
class="size-full border-0"
|
|
254
264
|
/>
|
|
@@ -256,7 +266,7 @@
|
|
|
256
266
|
v-else
|
|
257
267
|
class="flex h-full items-center justify-center bg-white text-sm text-gray-500"
|
|
258
268
|
>
|
|
259
|
-
ไม่พบไฟล์คู่มือ
|
|
269
|
+
{{ viewerError ? 'ไม่สามารถโหลดไฟล์ได้' : (current?.file ? 'กำลังโหลด...' : 'ไม่พบไฟล์คู่มือ') }}
|
|
260
270
|
</div>
|
|
261
271
|
</div>
|
|
262
272
|
</div>
|
|
@@ -333,6 +343,76 @@ const current = computed<IManual | null>(
|
|
|
333
343
|
() => props.manuals.find((m) => m.id === selectedId.value) ?? null,
|
|
334
344
|
)
|
|
335
345
|
|
|
346
|
+
// The API sets X-Frame-Options/CSP frame-ancestors that block embedding the
|
|
347
|
+
// PDF URL directly in an <iframe> across subdomains. Fetching the bytes and
|
|
348
|
+
// rendering via a blob: URL sidesteps that, since it isn't a framed request.
|
|
349
|
+
const viewerBlobUrl = ref<string | null>(null)
|
|
350
|
+
const viewerError = ref(false)
|
|
351
|
+
|
|
352
|
+
const revokeViewerBlobUrl = () => {
|
|
353
|
+
if (viewerBlobUrl.value) {
|
|
354
|
+
URL.revokeObjectURL(viewerBlobUrl.value)
|
|
355
|
+
viewerBlobUrl.value = null
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
watch(() => current.value?.file?.url, async (url) => {
|
|
360
|
+
revokeViewerBlobUrl()
|
|
361
|
+
viewerError.value = false
|
|
362
|
+
|
|
363
|
+
if (!url) return
|
|
364
|
+
|
|
365
|
+
try {
|
|
366
|
+
const res = await fetch(url)
|
|
367
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
|
368
|
+
|
|
369
|
+
const blob = await res.blob()
|
|
370
|
+
|
|
371
|
+
viewerBlobUrl.value = URL.createObjectURL(blob)
|
|
372
|
+
} catch {
|
|
373
|
+
viewerError.value = true
|
|
374
|
+
}
|
|
375
|
+
}, {
|
|
376
|
+
immediate: true,
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
onBeforeUnmount(revokeViewerBlobUrl)
|
|
380
|
+
|
|
381
|
+
const DEFAULT_WIDTH = 560
|
|
382
|
+
const MIN_WIDTH = 400
|
|
383
|
+
const MAX_WIDTH = 1100
|
|
384
|
+
|
|
385
|
+
const panelWidth = ref(DEFAULT_WIDTH)
|
|
386
|
+
|
|
387
|
+
const startResize = (e: MouseEvent) => {
|
|
388
|
+
e.preventDefault()
|
|
389
|
+
|
|
390
|
+
const startX = e.clientX
|
|
391
|
+
const startWidth = panelWidth.value
|
|
392
|
+
const prevCursor = document.body.style.cursor
|
|
393
|
+
const prevUserSelect = document.body.style.userSelect
|
|
394
|
+
|
|
395
|
+
document.body.style.cursor = 'col-resize'
|
|
396
|
+
document.body.style.userSelect = 'none'
|
|
397
|
+
|
|
398
|
+
const onMove = (moveEvent: MouseEvent) => {
|
|
399
|
+
const delta = startX - moveEvent.clientX
|
|
400
|
+
const maxWidth = Math.min(MAX_WIDTH, window.innerWidth)
|
|
401
|
+
|
|
402
|
+
panelWidth.value = Math.min(Math.max(startWidth + delta, MIN_WIDTH), maxWidth)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const onUp = () => {
|
|
406
|
+
document.body.style.cursor = prevCursor
|
|
407
|
+
document.body.style.userSelect = prevUserSelect
|
|
408
|
+
window.removeEventListener('mousemove', onMove)
|
|
409
|
+
window.removeEventListener('mouseup', onUp)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
window.addEventListener('mousemove', onMove)
|
|
413
|
+
window.addEventListener('mouseup', onUp)
|
|
414
|
+
}
|
|
415
|
+
|
|
336
416
|
const canManage = (app: string) =>
|
|
337
417
|
auth.isSuperAdmin.value || auth.hasPermission(app as UserModule, Permission.SUPER)
|
|
338
418
|
|