@michaelyagi/shoji 0.1.0-alpha.10 → 0.1.0-alpha.12
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/dist/esm/core/Gallery.d.ts +74 -2
- package/dist/esm/core/GestureController.d.ts +42 -6
- package/dist/esm/core/SlideManager.d.ts +27 -1
- package/dist/esm/core/dom.d.ts +1 -0
- package/dist/esm/core/index.js +274 -33
- package/dist/esm/core/index.js.map +1 -1
- package/dist/esm/core/types.d.ts +32 -3
- package/dist/esm/core/zoomTransition.d.ts +44 -7
- package/dist/esm/gestures/GestureEngine.d.ts +11 -0
- package/dist/esm/index.css +46 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/index2.css +14 -0
- package/dist/esm/plugins/autoplay/index.js +42 -1
- package/dist/esm/plugins/autoplay/index.js.map +1 -1
- package/dist/esm/plugins/rotateFlip/index.js +1 -1
- package/dist/esm/plugins/zoom/index.js +14 -2
- package/dist/esm/plugins/zoom/index.js.map +1 -1
- package/dist/esm/{zoomTransition-bbKHpVpA.js → zoomTransition-BeIimqDT.js} +27 -15
- package/dist/esm/zoomTransition-BeIimqDT.js.map +1 -0
- package/dist/shoji.css +60 -2
- package/dist/shoji.js +354 -49
- package/dist/shoji.js.map +1 -1
- package/dist/shoji.min.css +1 -1
- package/dist/shoji.min.js +1 -1
- package/dist/shoji.min.js.map +1 -1
- package/package.json +1 -1
- package/dist/esm/zoomTransition-bbKHpVpA.js.map +0 -1
|
@@ -30,6 +30,8 @@ export declare class Gallery {
|
|
|
30
30
|
private autoHidden;
|
|
31
31
|
private hoveredControlCount;
|
|
32
32
|
private isClosing;
|
|
33
|
+
/** True while a vertical drag has hidden controls past its own distance threshold (`setControlsHiddenForDrag`) — `onActivity()` defers to it, since the drag's own continuous pointermove stream would otherwise immediately re-reveal what it just hid on every single move. */
|
|
34
|
+
private controlsHiddenByDrag;
|
|
33
35
|
private itemList;
|
|
34
36
|
private scannedElements;
|
|
35
37
|
private activeIndex;
|
|
@@ -48,8 +50,22 @@ export declare class Gallery {
|
|
|
48
50
|
private pluginCleanups;
|
|
49
51
|
private readonly onContainerClick;
|
|
50
52
|
private readonly onOuterClick;
|
|
51
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* DESIGN.md §2.8 — any interaction re-shows controls, restarts the idle
|
|
55
|
+
* clock. `autoHideDelay: 0` = "never show controls" — a no-op here.
|
|
56
|
+
* `autoHideDelay: false` = "always visible" — also a no-op: nothing to
|
|
57
|
+
* reveal (already shown) or reschedule (no timer ever runs). Also a no-op
|
|
58
|
+
* once `isClosing` — a real bug, reported from real usage: moving the
|
|
59
|
+
* mouse during close()'s own controls-fade-then-zoom-out sequence (§2.6a)
|
|
60
|
+
* re-showed the just-hidden controls mid-animation, since these listeners
|
|
61
|
+
* stay wired for the whole close sequence. Same reasoning for
|
|
62
|
+
* `controlsHiddenByDrag` (§2.4/§2.8): a vertical drag's own `pointermove`
|
|
63
|
+
* stream would otherwise re-reveal what it just hid, every single frame.
|
|
64
|
+
*/
|
|
52
65
|
private readonly onActivity;
|
|
66
|
+
/** x */
|
|
67
|
+
private controlsHiddenAtGestureStart;
|
|
68
|
+
private readonly captureGestureStartState;
|
|
53
69
|
private readonly onKeydown;
|
|
54
70
|
constructor(target: HTMLElement | string, options?: GalleryOptions);
|
|
55
71
|
/** Everything the constructor does after `this.element` is resolved — shared with `reinit()` (§2.7). */
|
|
@@ -102,7 +118,19 @@ export declare class Gallery {
|
|
|
102
118
|
*/
|
|
103
119
|
getOriginElement(index: number): HTMLElement | null;
|
|
104
120
|
private scheduleAutoHide;
|
|
105
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Forces the same fade §2.8's idle timer would eventually trigger —
|
|
123
|
+
* public so a plugin can hide controls on its own trigger. Same
|
|
124
|
+
* `isControlActive()` guard as the timer. Also a no-op under
|
|
125
|
+
* `autoHideDelay: false` — a real bug, reported from real usage: Autoplay's
|
|
126
|
+
* tap-to-toggle-chrome behavior called this directly and ignored `false`
|
|
127
|
+
* entirely, since only the idle timer checked it. `false` has to hold for
|
|
128
|
+
* every caller, not just the timer — `mobileSettings.controls: false` is
|
|
129
|
+
* affected the same way, by design. `forceHideControls()`/
|
|
130
|
+
* `setControlsHiddenForDrag()` (close, drag-to-close) deliberately don't
|
|
131
|
+
* check this — direct user actions with their own feedback, not auto-hide.
|
|
132
|
+
*/
|
|
133
|
+
hideControls(): void;
|
|
106
134
|
private showControls;
|
|
107
135
|
/**
|
|
108
136
|
* DESIGN.md §2.1 — `caption` is `string | HTMLElement | DangerousHtmlCaption`:
|
|
@@ -173,8 +201,52 @@ export declare class Gallery {
|
|
|
173
201
|
*/
|
|
174
202
|
private applyMobileControlsSetting;
|
|
175
203
|
close(): void;
|
|
204
|
+
/**
|
|
205
|
+
* DESIGN.md §2.4/§2.6a — same effect as `close()`, from a completed
|
|
206
|
+
* vertical swipe. `frozenDrag` is threaded through to `zoomOut()`'s own
|
|
207
|
+
* `dragStart`, so the whole close continues as one motion from exactly
|
|
208
|
+
* where the drag left off (see `GestureController.
|
|
209
|
+
* takeFrozenDragTransform`).
|
|
210
|
+
*/
|
|
211
|
+
private closeFromSwipe;
|
|
212
|
+
/**
|
|
213
|
+
* `frozenDrag` — see `closeFromSwipe()`'s doc comment; absent for a
|
|
214
|
+
* button-close, which has no drag to continue from. Controls fade and the
|
|
215
|
+
* zoom-out run concurrently, both starting the instant `forceHideControls()`
|
|
216
|
+
* runs — not sequenced one after the other. (A previous version waited for
|
|
217
|
+
* the controls' own fade to fully finish before starting the zoom-out, for
|
|
218
|
+
* a button-close specifically — requested directly, to avoid stationary
|
|
219
|
+
* chrome hovering over an already-shrinking photo. Reversed on later,
|
|
220
|
+
* explicit feedback: waiting read as two distinct steps rather than one
|
|
221
|
+
* motion; starting both together still avoids stationary chrome, since
|
|
222
|
+
* the chrome is disappearing too, just without the pause.)
|
|
223
|
+
*/
|
|
224
|
+
private beginClose;
|
|
225
|
+
/** Forces the same fade §2.8's idle timer would eventually trigger, bypassing `hideControls()`'s own `isControlActive()` hover guard — the most common close path (clicking close) is hovering a control at this exact instant, and a deliberate close should hide regardless. No-op if already hidden. */
|
|
226
|
+
private forceHideControls;
|
|
227
|
+
/**
|
|
228
|
+
* DESIGN.md §2.4/§2.8 — `GestureController`'s live vertical-drag cue: hide
|
|
229
|
+
* past the same distance a release would close, reveal again on retreat.
|
|
230
|
+
* `hidden: false` only reveals if visible when *this* gesture started
|
|
231
|
+
* (`controlsHiddenAtGestureStart`) — shouldn't resurrect controls already
|
|
232
|
+
* separately hidden (idle, or `autoHideDelay: 0`) before the drag began.
|
|
233
|
+
* `dragCloseThreshold` (bus event) fires on every crossing regardless of
|
|
234
|
+
* that guard — Autoplay (§4-autoplay) needs to know about the crossing
|
|
235
|
+
* itself, not just whether controls visibly moved.
|
|
236
|
+
*/
|
|
237
|
+
private setControlsHiddenForDrag;
|
|
176
238
|
/** `item.width`/`height`, else origin's `naturalWidth`/`naturalHeight` (accurate when `item.thumb` is unset). Feeds `computeTransform`'s letterbox-aware sizing. */
|
|
177
239
|
private resolveAspectRatio;
|
|
240
|
+
/**
|
|
241
|
+
* `item.width`/`height` only — deliberately never a thumbnail's own
|
|
242
|
+
* `naturalWidth`/`naturalHeight` the way `resolveAspectRatio` above will:
|
|
243
|
+
* that's a fine stand-in for *shape*, but using it as the real photo's
|
|
244
|
+
* true pixel size would under-cap a genuinely large photo down to
|
|
245
|
+
* thumbnail resolution. `undefined` here just means "genuinely unknown,"
|
|
246
|
+
* not "assume small" — `zoomTransition.ts`'s `containedBox` already
|
|
247
|
+
* treats it that way (no cap applied at all).
|
|
248
|
+
*/
|
|
249
|
+
private resolveNaturalSize;
|
|
178
250
|
/**
|
|
179
251
|
* Idempotent on purpose: a pending zoom-out's `transitionend`/fallback
|
|
180
252
|
* timeout can still fire after `destroy()` has already force-finished the
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { GestureEngineOptions } from '../gestures/GestureEngine';
|
|
2
2
|
import { SlideManager } from './SlideManager';
|
|
3
|
+
import { FrozenDragTransform } from './zoomTransition';
|
|
3
4
|
/**
|
|
4
|
-
* A real interactive control
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* A real interactive control, plus `.shoji-caption` — a click/drag starting
|
|
6
|
+
* there should select/scroll its text natively, not get captured as a
|
|
7
|
+
* gesture. Also used by `Gallery.ts`'s `isBackdropClick`, which used to have
|
|
8
|
+
* its own narrower list (missing select/input/textarea/a[href]) — a
|
|
9
|
+
* plugin-mounted non-button control got misread as a backdrop click and
|
|
10
|
+
* closed the gallery. One shared selector so that can't recur.
|
|
8
11
|
*/
|
|
9
|
-
export declare const INTERACTIVE_CONTROL_SELECTOR = "button, video, input, select, textarea, a[href], [data-shoji-no-drag]";
|
|
12
|
+
export declare const INTERACTIVE_CONTROL_SELECTOR = "button, video, input, select, textarea, a[href], [data-shoji-no-drag], .shoji-caption";
|
|
10
13
|
/** What `GestureController` needs from `Gallery` — narrow on purpose, so this module never reaches into Gallery internals beyond this contract. */
|
|
11
14
|
export interface GestureControllerHost {
|
|
12
15
|
dialog: HTMLElement;
|
|
@@ -16,6 +19,10 @@ export interface GestureControllerHost {
|
|
|
16
19
|
next(): void;
|
|
17
20
|
prev(): void;
|
|
18
21
|
close(): void;
|
|
22
|
+
/** Same effect as `close()`, but for a completed vertical swipe — skips the separate controls-fade pause in front of the zoom-out (see `finishVerticalDrag`). */
|
|
23
|
+
closeFromSwipe(frozenDrag: FrozenDragTransform): void;
|
|
24
|
+
/** Live, reversible — crossing the close threshold mid-drag hides the toolbar/nav/counter/caption (a "let go now and this closes" cue); dragging back under it before release reveals them again. See `applyVerticalDragFeedback`. */
|
|
25
|
+
setControlsHiddenForDrag(hidden: boolean): void;
|
|
19
26
|
/** `closable: false` (GalleryOptions) — false suspends vertical drag-to-close entirely: no live feedback, release never calls `close()`. */
|
|
20
27
|
canClose(): boolean;
|
|
21
28
|
onActivity(): void;
|
|
@@ -42,6 +49,10 @@ export interface GestureRelayCallbacks {
|
|
|
42
49
|
export declare class GestureController {
|
|
43
50
|
private readonly host;
|
|
44
51
|
private readonly engine;
|
|
52
|
+
/** Same distance `GestureEngine` itself uses to decide a release would complete the close — reused here so the live controls-hide cue in `applyVerticalDragFeedback` actually means what it visually claims. */
|
|
53
|
+
private readonly controlsHideThreshold;
|
|
54
|
+
private controlsHiddenForDrag;
|
|
55
|
+
private lastDragDelta;
|
|
45
56
|
constructor(host: GestureControllerHost, relay: GestureRelayCallbacks, options?: Partial<GestureEngineOptions>);
|
|
46
57
|
destroy(): void;
|
|
47
58
|
/** Horizontal follows the finger 1:1; vertical drives close-feedback instead. Axis-locked, one branch per drag. */
|
|
@@ -50,8 +61,33 @@ export declare class GestureController {
|
|
|
50
61
|
/** `completed` decides intent; `canGoNext`/`canGoPrev` guard whether that's actually possible (`loop: false` at an end item). */
|
|
51
62
|
private finishHorizontalDrag;
|
|
52
63
|
private settleDragOffset;
|
|
53
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* Purely presentational — scales/fades the *image* as it's dragged away,
|
|
66
|
+
* without closing until release decides. Applied to `host.slides.element`,
|
|
67
|
+
* not `host.dialog` — requested directly: toolbar/nav/counter/caption are
|
|
68
|
+
* siblings, not descendants, so they stay anchored, not moving with it.
|
|
69
|
+
*/
|
|
54
70
|
private applyVerticalDragFeedback;
|
|
55
71
|
private clearVerticalDragFeedback;
|
|
56
72
|
private finishVerticalDrag;
|
|
73
|
+
/**
|
|
74
|
+
* Reads the drag's last live appearance, instantly resets `.shoji-slides`
|
|
75
|
+
* back to neutral (nothing left on it to visibly snap — `Gallery` bakes
|
|
76
|
+
* these same values onto the photo itself in the same synchronous tick,
|
|
77
|
+
* so the rendered result is unchanged, just re-homed), and returns them
|
|
78
|
+
* for that hand-off. Translate is the raw, unclamped drag distance —
|
|
79
|
+
* requested directly: the close animation must continue from exactly
|
|
80
|
+
* where the drag left off, full stop, not recenter first. (A previous
|
|
81
|
+
* version clamped this to the same 160px the dim/scale feedback ramps
|
|
82
|
+
* over, to bound how far away the close animation could start — but
|
|
83
|
+
* clamping is itself an instant correction: for any drag past that
|
|
84
|
+
* distance, release visibly snapped the photo from wherever it actually
|
|
85
|
+
* was back to the clamped point, before the real shrink-to-thumbnail
|
|
86
|
+
* motion continued from there. That snap — not the shrink itself — is
|
|
87
|
+
* what reads as "jumps to a small image in the middle of the screen."
|
|
88
|
+
* Reported from real usage, confirmed on video: released past the clamp,
|
|
89
|
+
* the photo visibly jumped from off-screen back to near-center in a
|
|
90
|
+
* single frame.)
|
|
91
|
+
*/
|
|
92
|
+
private takeFrozenDragTransform;
|
|
57
93
|
}
|
|
@@ -66,7 +66,33 @@ export declare class SlideManager {
|
|
|
66
66
|
private moveIn;
|
|
67
67
|
/** Starts decoding `item` for `index`, only if nothing already is (see `pending`). Resolves by looking up whichever slot currently wants this index, not the one active when the decode started. */
|
|
68
68
|
private ensureImageDecoding;
|
|
69
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* DESIGN.md §2.3 — swaps the spinner for the placeholder once *it*
|
|
71
|
+
* decodes, not immediately: `item.thumb` is often just `item.src` again,
|
|
72
|
+
* so an undecoded placeholder can leave as long a blank gap as the
|
|
73
|
+
* spinner it replaces.
|
|
74
|
+
*
|
|
75
|
+
* A real bug: `.shoji-slide-open-placeholder`'s CSS unconditionally
|
|
76
|
+
* force-fills the frame (deliberate default: a real photo is usually
|
|
77
|
+
* bigger than the dialog). Wrong for a genuinely small photo with known
|
|
78
|
+
* `item.width`/`item.height` — same "grows too big, snaps down once real"
|
|
79
|
+
* symptom the zoom-in transition itself had (§2.3b), but that fix only
|
|
80
|
+
* governs the animated *transform*, not this placeholder's own CSS box
|
|
81
|
+
* once it settles. When `naturalSize` is known, sizes the placeholder
|
|
82
|
+
* explicitly instead, reusing `containedBox` (`zoomTransition.ts`) —
|
|
83
|
+
* unknown-size items keep the original force-fill guess.
|
|
84
|
+
*
|
|
85
|
+
* A second real bug in that same fix: measured `slot.media`'s own rect as
|
|
86
|
+
* "the container" — but `slot.media` is the exact element the zoom-in
|
|
87
|
+
* transition (§2.3b) animates a `scale()` transform on. `getBoundingClientRect()`
|
|
88
|
+
* reflects whatever transform is *currently* applied, and the placeholder's
|
|
89
|
+
* own decode typically resolves before that ~300ms animation settles — so
|
|
90
|
+
* this measured a still-small, mid-animation rect (often the origin
|
|
91
|
+
* thumbnail's own tiny size) instead of the real dialog size. `slot.root`
|
|
92
|
+
* (`.shoji-slide`) fixes it: same box, only ever `translateX`'d for
|
|
93
|
+
* pool-offset positioning, never scaled — a stable read regardless of
|
|
94
|
+
* what its child is mid-animation through.
|
|
95
|
+
*/
|
|
70
96
|
private revealOpenPlaceholder;
|
|
71
97
|
/** Native `<video controls>` — real playback, not just a poster. Not autoplayed/muted: a deliberate click-to-play, not a background loop. */
|
|
72
98
|
private renderVideo;
|