@marianmeres/stuic 3.102.0 → 3.104.0

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.
@@ -418,11 +418,17 @@
418
418
  let swipeStartTime = 0;
419
419
  let isSwiping = false;
420
420
 
421
- // ---- Drag detection (for page click vs drag/swipe discrimination) ----
422
-
423
- let _wasDragged = false;
424
- let _dragStartClientX = 0;
425
- let _dragStartClientY = 0;
421
+ // ---- Tap vs drag discrimination for page/area activation ----
422
+ // Activation uses pointer events (pointerdown/pointerup) rather than a synthesized
423
+ // mouse `click`. Pointer events unify mouse + touch and fire reliably on a clean tap,
424
+ // even on the `touch-action: none` gesture surface where tap→click synthesis is
425
+ // unreliable (notably iOS Safari, worst of all on inner SVG shapes). A tap is a
426
+ // pointerup landing within TAP_SLOP_PX of its pointerdown; movement beyond that is a
427
+ // swipe/drag and does not activate.
428
+
429
+ const TAP_SLOP_PX = 10;
430
+ let _tapDownX = 0;
431
+ let _tapDownY = 0;
426
432
 
427
433
  // ---- Zoom helpers ----
428
434
 
@@ -572,10 +578,6 @@
572
578
  const clientX = "touches" in e ? e.touches[0].clientX : e.clientX;
573
579
  const clientY = "touches" in e ? e.touches[0].clientY : e.clientY;
574
580
 
575
- _wasDragged = false;
576
- _dragStartClientX = clientX;
577
- _dragStartClientY = clientY;
578
-
579
581
  if (zoomLevel > 1) {
580
582
  // Pan mode
581
583
  e.preventDefault();
@@ -603,18 +605,6 @@
603
605
  return;
604
606
  }
605
607
 
606
- // Track drag for page click detection
607
- if (!_wasDragged) {
608
- const cx = "touches" in e ? (e.touches[0]?.clientX ?? 0) : e.clientX;
609
- const cy = "touches" in e ? (e.touches[0]?.clientY ?? 0) : e.clientY;
610
- if (
611
- Math.abs(cx - _dragStartClientX) > 5 ||
612
- Math.abs(cy - _dragStartClientY) > 5
613
- ) {
614
- _wasDragged = true;
615
- }
616
- }
617
-
618
608
  // Pan
619
609
  if (isPanning) {
620
610
  e.preventDefault();
@@ -693,10 +683,21 @@
693
683
  };
694
684
  }
695
685
 
686
+ // ---- Tap (pointer) helpers for page/area activation ----
687
+
688
+ function handleTapDown(e: PointerEvent) {
689
+ _tapDownX = e.clientX;
690
+ _tapDownY = e.clientY;
691
+ }
692
+
693
+ function isTap(e: PointerEvent): boolean {
694
+ return Math.hypot(e.clientX - _tapDownX, e.clientY - _tapDownY) <= TAP_SLOP_PX;
695
+ }
696
+
696
697
  // ---- Page click ----
697
698
 
698
- function handlePageClick(e: MouseEvent, page: BookPage | undefined) {
699
- if (!onPageClick || !page || _wasDragged) return;
699
+ function handlePageClick(e: PointerEvent, page: BookPage | undefined) {
700
+ if (!onPageClick || !page || !isTap(e)) return;
700
701
  const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
701
702
  const x = (e.clientX - rect.left) / rect.width;
702
703
  const y = (e.clientY - rect.top) / rect.height;
@@ -769,7 +770,8 @@
769
770
  <div
770
771
  class={twMerge(!unstyled && "stuic-book-sheet-front", classPage)}
771
772
  data-placeholder={!sheet.frontPage && sheet.backPage ? "" : undefined}
772
- onclick={(e) => handlePageClick(e, sheet.frontPage)}
773
+ onpointerdown={handleTapDown}
774
+ onpointerup={(e) => handlePageClick(e, sheet.frontPage)}
773
775
  >
774
776
  {#if isNearby && sheet.frontPage}
775
777
  {#if renderPage}
@@ -840,8 +842,9 @@
840
842
  width={area.w}
841
843
  height={area.h}
842
844
  class={!unstyled ? "stuic-book-area" : undefined}
843
- onclick={(e: MouseEvent) => {
844
- if (_wasDragged) return;
845
+ onpointerdown={handleTapDown}
846
+ onpointerup={(e: PointerEvent) => {
847
+ if (!isTap(e)) return;
845
848
  e.stopPropagation();
846
849
  onAreaClick({ area, page: sheet.frontPage! });
847
850
  }}
@@ -858,7 +861,8 @@
858
861
  <div
859
862
  class={twMerge(!unstyled && "stuic-book-sheet-back", classPage)}
860
863
  data-placeholder={!sheet.backPage && sheet.frontPage ? "" : undefined}
861
- onclick={(e) => handlePageClick(e, sheet.backPage)}
864
+ onpointerdown={handleTapDown}
865
+ onpointerup={(e) => handlePageClick(e, sheet.backPage)}
862
866
  >
863
867
  {#if isNearby && sheet.backPage}
864
868
  {#if renderPage}
@@ -925,8 +929,9 @@
925
929
  width={area.w}
926
930
  height={area.h}
927
931
  class={!unstyled ? "stuic-book-area" : undefined}
928
- onclick={(e: MouseEvent) => {
929
- if (_wasDragged) return;
932
+ onpointerdown={handleTapDown}
933
+ onpointerup={(e: PointerEvent) => {
934
+ if (!isTap(e)) return;
930
935
  e.stopPropagation();
931
936
  onAreaClick({ area, page: sheet.backPage! });
932
937
  }}
@@ -389,30 +389,36 @@
389
389
  ============================================================================ */
390
390
  .stuic-button[data-aspect1] {
391
391
  aspect-ratio: 1;
392
- justify-items: center;
393
392
  }
394
393
 
394
+ /*
395
+ * Declare both dimensions instead of deriving the square from aspect-ratio +
396
+ * min-* on a shrink-to-fit inline-flex box. WebKit (iOS Safari) resolves the
397
+ * cross-axis from content width before enforcing min-width, which squeezed
398
+ * icon buttons into a vertical ellipse. Fixed width == height removes that
399
+ * derivation, so every engine renders a true square/circle.
400
+ */
395
401
  .stuic-button[data-aspect1][data-size="sm"] {
396
- min-width: var(--stuic-button-min-height-sm);
397
- min-height: var(--stuic-button-min-height-sm);
402
+ width: var(--stuic-button-min-height-sm);
403
+ height: var(--stuic-button-min-height-sm);
398
404
  padding: var(--stuic-button-padding-y-sm);
399
405
  }
400
406
 
401
407
  .stuic-button[data-aspect1][data-size="md"] {
402
- min-width: var(--stuic-button-min-height-md);
403
- min-height: var(--stuic-button-min-height-md);
408
+ width: var(--stuic-button-min-height-md);
409
+ height: var(--stuic-button-min-height-md);
404
410
  padding: var(--stuic-button-padding-y-md);
405
411
  }
406
412
 
407
413
  .stuic-button[data-aspect1][data-size="lg"] {
408
- min-width: var(--stuic-button-min-height-lg);
409
- min-height: var(--stuic-button-min-height-lg);
414
+ width: var(--stuic-button-min-height-lg);
415
+ height: var(--stuic-button-min-height-lg);
410
416
  padding: var(--stuic-button-padding-y-lg);
411
417
  }
412
418
 
413
419
  .stuic-button[data-aspect1][data-size="xl"] {
414
- min-width: var(--stuic-button-min-height-xl);
415
- min-height: var(--stuic-button-min-height-xl);
420
+ width: var(--stuic-button-min-height-xl);
421
+ height: var(--stuic-button-min-height-xl);
416
422
  padding: var(--stuic-button-padding-y-xl);
417
423
  }
418
424
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "3.102.0",
3
+ "version": "3.104.0",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && pnpm run prepack",
@@ -69,7 +69,7 @@
69
69
  "prettier": "^3.8.3",
70
70
  "prettier-plugin-svelte": "^3.5.2",
71
71
  "publint": "^0.3.21",
72
- "svelte": "^5.55.10",
72
+ "svelte": "^5.56.0",
73
73
  "svelte-check": "^4.4.8",
74
74
  "tailwindcss": "^4.3.0",
75
75
  "tsx": "^4.22.3",
@@ -81,14 +81,14 @@
81
81
  "dependencies": {
82
82
  "@marianmeres/clog": "^3.21.0",
83
83
  "@marianmeres/cron": "^2.0.1",
84
- "@marianmeres/design-tokens": "^1.7.0",
84
+ "@marianmeres/design-tokens": "^1.8.0",
85
85
  "@marianmeres/icons-fns": "^5.0.0",
86
86
  "@marianmeres/item-collection": "^1.4.2",
87
87
  "@marianmeres/paging-store": "^2.1.1",
88
88
  "@marianmeres/parse-boolean": "^2.1.0",
89
89
  "@marianmeres/ticker": "^1.17.1",
90
90
  "@marianmeres/tree": "^2.3.0",
91
- "libphonenumber-js": "^1.13.3",
91
+ "libphonenumber-js": "^1.13.4",
92
92
  "runed": "^0.23.4",
93
93
  "tailwind-merge": "^3.6.0"
94
94
  }