@hanology/cham-browser 0.4.30 → 0.4.32

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanology/cham-browser",
3
- "version": "0.4.30",
3
+ "version": "0.4.32",
4
4
  "description": "CHAM — browser-compatible parser, serializer, and site generator for Classical Han Annotated Markdown",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -168,9 +168,8 @@ describe('useAnnotationTooltip', () => {
168
168
  left: 200, top: 300, bottom: 330, right: 250, width: 50, height: 30,
169
169
  })
170
170
  show(createMockEvent(target), [makeAnn({ id: 'a1', kind: 'semantic' })])
171
- // Default layout is vertical — positioned via right + top
172
- expect(style.value.right).toBeTruthy()
173
- expect(style.value.top).toBe('50%')
174
- expect(style.value.transform).toBe('translateY(-50%)')
171
+ // Default layout is vertical — positioned via left + top
172
+ expect(style.value.left).toBeTruthy()
173
+ expect(style.value.top).toBeTruthy()
175
174
  })
176
175
  })
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { ref, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'
2
+ import { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'
3
3
  import { annotationToPronSegment } from '../utils/annotationParser'
4
4
  import { toChineseNumber } from '../utils/chineseNumber'
5
5
  import PronunciationGroup from './PronunciationGroup.vue'
@@ -21,6 +21,10 @@ const emit = defineEmits<{
21
21
 
22
22
  const bodyRef = ref<HTMLElement | null>(null)
23
23
 
24
+ const ww = ref(typeof window !== 'undefined' ? window.innerWidth : 1024)
25
+ const isMobile = computed(() => ww.value < 768)
26
+ function onResize() { ww.value = window.innerWidth }
27
+
24
28
  // ─── Resize ───
25
29
  const paneWidth = ref(320)
26
30
  const MIN_W = 180
@@ -36,7 +40,7 @@ function initWidth() {
36
40
  return
37
41
  }
38
42
  } catch {}
39
- paneWidth.value = props.vertical ? 240 : 320
43
+ paneWidth.value = props.vertical ? (ww.value < 768 ? Math.round(ww.value * 0.65) : 240) : 320
40
44
  }
41
45
 
42
46
  let resizing = false
@@ -137,10 +141,12 @@ watch(() => props.activeId, async (id) => {
137
141
  onMounted(() => {
138
142
  initWidth()
139
143
  document.addEventListener('keydown', onKeydown)
144
+ window.addEventListener('resize', onResize, { passive: true })
140
145
  })
141
146
 
142
147
  onBeforeUnmount(() => {
143
148
  document.removeEventListener('keydown', onKeydown)
149
+ window.removeEventListener('resize', onResize)
144
150
  if (moveFn) {
145
151
  document.removeEventListener('mousemove', moveFn)
146
152
  document.removeEventListener('touchmove', moveFn)
@@ -154,6 +160,13 @@ onBeforeUnmount(() => {
154
160
 
155
161
  <template>
156
162
  <Teleport to="body">
163
+ <Transition name="ann-dim">
164
+ <div
165
+ v-if="visible && annotations.length && vertical && isMobile"
166
+ class="ann-pane-dim"
167
+ @click="emit('close')"
168
+ />
169
+ </Transition>
157
170
  <Transition name="ann-pane">
158
171
  <div
159
172
  v-if="visible && annotations.length"
@@ -165,7 +178,7 @@ onBeforeUnmount(() => {
165
178
  <span class="ann-pane-title">注釋</span>
166
179
  <span class="ann-pane-count">{{ annotations.length }}</span>
167
180
  <button class="ann-pane-close" @click="emit('close')" aria-label="關閉">
168
- <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><path d="M18 6L6 18M6 6l12 12"/></svg>
181
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M18 6L6 18M6 6l12 12"/></svg>
169
182
  </button>
170
183
  </div>
171
184
  <div ref="bodyRef" class="ann-pane-body">
@@ -404,6 +417,21 @@ onBeforeUnmount(() => {
404
417
  height: 48px;
405
418
  }
406
419
 
420
+ /* ─── Backdrop dim (mobile vertical) ─── */
421
+ .ann-pane-dim {
422
+ position: fixed;
423
+ inset: 0;
424
+ background: rgba(var(--shadow-rgb), 0.24);
425
+ backdrop-filter: blur(4px);
426
+ -webkit-backdrop-filter: blur(4px);
427
+ z-index: 299;
428
+ }
429
+
430
+ .ann-dim-enter-active { transition: opacity 0.3s ease; }
431
+ .ann-dim-leave-active { transition: opacity 0.15s ease; }
432
+ .ann-dim-enter-from,
433
+ .ann-dim-leave-to { opacity: 0; }
434
+
407
435
  /* ─── Transition ─── */
408
436
  .ann-pane-enter-active {
409
437
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
@@ -521,7 +549,14 @@ onBeforeUnmount(() => {
521
549
 
522
550
  .ann-pane.vertical .ann-pane-close {
523
551
  margin-left: 0;
524
- margin-top: auto;
552
+ margin-top: 0;
553
+ margin-bottom: 12px;
554
+ order: -1;
555
+ width: 32px;
556
+ height: 32px;
557
+ border: 1px solid var(--border);
558
+ border-radius: 6px;
559
+ background: var(--surface);
525
560
  }
526
561
 
527
562
  .ann-pane.vertical .ann-pane-count,
@@ -536,7 +571,7 @@ onBeforeUnmount(() => {
536
571
 
537
572
  @media (max-width: 768px) {
538
573
  .ann-pane.vertical {
539
- width: 80vw !important;
574
+ width: 65vw !important;
540
575
  height: 100vh !important;
541
576
  max-height: none !important;
542
577
  top: 0 !important;
@@ -546,10 +581,18 @@ onBeforeUnmount(() => {
546
581
  border-top: none !important;
547
582
  border-radius: 0 !important;
548
583
  box-shadow: -4px 0 24px rgba(var(--shadow-rgb), 0.06) !important;
584
+ z-index: 300;
549
585
  }
550
586
  .ann-pane.vertical .ann-pane-handle {
551
587
  display: flex !important;
552
588
  }
589
+ .ann-pane.vertical .ann-pane-body {
590
+ padding: 20px 0;
591
+ }
592
+ .ann-pane.vertical .ann-pane-close {
593
+ width: 36px;
594
+ height: 36px;
595
+ }
553
596
  .ann-pane.vertical.ann-pane-enter-from,
554
597
  .ann-pane.vertical.ann-pane-leave-to {
555
598
  transform: translateX(-100%) !important;