@rixl/media-core 1.0.2 → 1.1.1
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/README.md +74 -84
- package/dist/chunks/B-QvSqjj.js +1 -0
- package/dist/chunks/B2DDE32u.js +1 -0
- package/dist/chunks/DMkwQjgA.js +1 -0
- package/dist/index.d.ts +1906 -82
- package/dist/index.js +6 -73
- package/package.json +13 -6
- package/dist/api/analytics.d.ts +0 -16
- package/dist/api/analytics.js +0 -227
- package/dist/api/feed.js +0 -52
- package/dist/api/heatmap.d.ts +0 -6
- package/dist/api/heatmap.js +0 -28
- package/dist/api/hot-segments.d.ts +0 -7
- package/dist/api/hot-segments.js +0 -20
- package/dist/api/image.js +0 -11
- package/dist/api/rixl-client.d.ts +0 -3
- package/dist/api/rixl-client.js +0 -14
- package/dist/api/upload.d.ts +0 -76
- package/dist/api/upload.js +0 -150
- package/dist/api/video.js +0 -11
- package/dist/lib/analytics-builders.d.ts +0 -59
- package/dist/lib/analytics-builders.js +0 -88
- package/dist/lib/analytics-initialize.d.ts +0 -1
- package/dist/lib/analytics-initialize.js +0 -59
- package/dist/lib/analytics-utils.d.ts +0 -43
- package/dist/lib/analytics-utils.js +0 -85
- package/dist/lib/analytics-wire.js +0 -153
- package/dist/lib/buffer-progress.d.ts +0 -20
- package/dist/lib/buffer-progress.js +0 -37
- package/dist/lib/caption-styles.d.ts +0 -20
- package/dist/lib/caption-styles.js +0 -56
- package/dist/lib/caption-utils.d.ts +0 -80
- package/dist/lib/caption-utils.js +0 -111
- package/dist/lib/chapter-utils.d.ts +0 -102
- package/dist/lib/chapter-utils.js +0 -147
- package/dist/lib/click-disambiguation.d.ts +0 -44
- package/dist/lib/click-disambiguation.js +0 -80
- package/dist/lib/click-outside.d.ts +0 -68
- package/dist/lib/click-outside.js +0 -64
- package/dist/lib/colors.d.ts +0 -6
- package/dist/lib/colors.js +0 -16
- package/dist/lib/content-analytics.d.ts +0 -27
- package/dist/lib/content-analytics.js +0 -38
- package/dist/lib/environment-effects.d.ts +0 -89
- package/dist/lib/environment-effects.js +0 -139
- package/dist/lib/feed-autoscroll.d.ts +0 -10
- package/dist/lib/feed-autoscroll.js +0 -13
- package/dist/lib/feed-container-height.d.ts +0 -7
- package/dist/lib/feed-container-height.js +0 -11
- package/dist/lib/feed-description-overflow.d.ts +0 -1
- package/dist/lib/feed-description-overflow.js +0 -9
- package/dist/lib/feed-dwell.d.ts +0 -2
- package/dist/lib/feed-dwell.js +0 -8
- package/dist/lib/feed-loop.d.ts +0 -10
- package/dist/lib/feed-loop.js +0 -21
- package/dist/lib/feed-scroll.d.ts +0 -11
- package/dist/lib/feed-scroll.js +0 -19
- package/dist/lib/feed-transforms.d.ts +0 -5
- package/dist/lib/feed-transforms.js +0 -30
- package/dist/lib/filter-undefined.js +0 -11
- package/dist/lib/fullscreen-utils.d.ts +0 -1
- package/dist/lib/fullscreen-utils.js +0 -7
- package/dist/lib/hls-buffer-flush.js +0 -46
- package/dist/lib/hls-error-recovery.d.ts +0 -35
- package/dist/lib/hls-error-recovery.js +0 -75
- package/dist/lib/hls-event-handlers.js +0 -61
- package/dist/lib/hls-loader.js +0 -23
- package/dist/lib/hls-setup.d.ts +0 -72
- package/dist/lib/hls-setup.js +0 -144
- package/dist/lib/hls-utils.d.ts +0 -31
- package/dist/lib/hls-utils.js +0 -34
- package/dist/lib/image-render.d.ts +0 -29
- package/dist/lib/image-render.js +0 -63
- package/dist/lib/keyboard-config.d.ts +0 -66
- package/dist/lib/keyboard-config.js +0 -127
- package/dist/lib/keyboard-utils.d.ts +0 -4
- package/dist/lib/keyboard-utils.js +0 -11
- package/dist/lib/lifecycle-effects.d.ts +0 -67
- package/dist/lib/lifecycle-effects.js +0 -78
- package/dist/lib/loading-debounce.d.ts +0 -26
- package/dist/lib/loading-debounce.js +0 -38
- package/dist/lib/numbers.d.ts +0 -6
- package/dist/lib/numbers.js +0 -36
- package/dist/lib/pip-utils.d.ts +0 -16
- package/dist/lib/pip-utils.js +0 -32
- package/dist/lib/playback-handlers.d.ts +0 -33
- package/dist/lib/playback-handlers.js +0 -51
- package/dist/lib/playback-slider.d.ts +0 -23
- package/dist/lib/playback-slider.js +0 -24
- package/dist/lib/player-labels.d.ts +0 -23
- package/dist/lib/player-labels.js +0 -132
- package/dist/lib/player-menu-labels.d.ts +0 -80
- package/dist/lib/player-menu-labels.js +0 -829
- package/dist/lib/player-ui-actions.d.ts +0 -10
- package/dist/lib/player-ui-actions.js +0 -58
- package/dist/lib/progress-fraction.d.ts +0 -11
- package/dist/lib/progress-fraction.js +0 -20
- package/dist/lib/progress-persist.d.ts +0 -28
- package/dist/lib/progress-persist.js +0 -54
- package/dist/lib/segment-analytics.d.ts +0 -20
- package/dist/lib/segment-analytics.js +0 -29
- package/dist/lib/subtitle-logic.d.ts +0 -31
- package/dist/lib/subtitle-logic.js +0 -75
- package/dist/lib/subtitle-utils.d.ts +0 -16
- package/dist/lib/subtitle-utils.js +0 -37
- package/dist/lib/time.d.ts +0 -1
- package/dist/lib/time.js +0 -17
- package/dist/lib/track-persistence.d.ts +0 -4
- package/dist/lib/track-persistence.js +0 -35
- package/dist/lib/video-preload-config.d.ts +0 -34
- package/dist/lib/video-preload-config.js +0 -41
- package/dist/lib/video-spinner.d.ts +0 -25
- package/dist/lib/video-spinner.js +0 -35
- package/dist/lib/video-stats.d.ts +0 -28
- package/dist/lib/video-stats.js +0 -44
- package/dist/lib/volume-utils.d.ts +0 -59
- package/dist/lib/volume-utils.js +0 -55
- package/dist/progressbar.d.ts +0 -10
- package/dist/progressbar.js +0 -125
- package/dist/store/analytics-context-store.d.ts +0 -4
- package/dist/store/analytics-context-store.js +0 -13
- package/dist/store/analytics-device-store.d.ts +0 -8
- package/dist/store/analytics-device-store.js +0 -44
- package/dist/store/analytics-session-store.d.ts +0 -8
- package/dist/store/analytics-session-store.js +0 -40
- package/dist/store/analytics-store.d.ts +0 -30
- package/dist/store/analytics-store.js +0 -88
- package/dist/store/controls-store.d.ts +0 -16
- package/dist/store/controls-store.js +0 -53
- package/dist/store/feed-cache-store.d.ts +0 -24
- package/dist/store/feed-cache-store.js +0 -165
- package/dist/store/feed-interaction-store.d.ts +0 -17
- package/dist/store/feed-interaction-store.js +0 -32
- package/dist/store/feed-store.d.ts +0 -16
- package/dist/store/feed-store.js +0 -38
- package/dist/store/generic-store.js +0 -24
- package/dist/store/image-store.d.ts +0 -2
- package/dist/store/image-store.js +0 -8
- package/dist/store/player-container-element-store.d.ts +0 -10
- package/dist/store/player-container-element-store.js +0 -38
- package/dist/store/player-global-actions.js +0 -53
- package/dist/store/player-media-element-store.d.ts +0 -10
- package/dist/store/player-media-element-store.js +0 -38
- package/dist/store/player-store-actions.js +0 -197
- package/dist/store/player-store.d.ts +0 -94
- package/dist/store/player-store.js +0 -127
- package/dist/store/player-track-actions.js +0 -44
- package/dist/store/postStore.d.ts +0 -20
- package/dist/store/postStore.js +0 -112
- package/dist/store/settings/constants.d.ts +0 -15
- package/dist/store/settings/constants.js +0 -47
- package/dist/store/settings/defaults.d.ts +0 -2
- package/dist/store/settings/defaults.js +0 -24
- package/dist/store/settings/storage.d.ts +0 -3
- package/dist/store/settings/storage.js +0 -28
- package/dist/store/settings/store.d.ts +0 -32
- package/dist/store/settings/store.js +0 -59
- package/dist/store/settings/types.d.ts +0 -27
- package/dist/store/settings/validation.d.ts +0 -7
- package/dist/store/settings/validation.js +0 -55
- package/dist/store/settings-menu-store.d.ts +0 -19
- package/dist/store/settings-menu-store.js +0 -30
- package/dist/store/video-store.d.ts +0 -2
- package/dist/store/video-store.js +0 -8
- package/dist/types/analytics.d.ts +0 -113
- package/dist/types/feed.d.ts +0 -21
- package/dist/types/file.d.ts +0 -12
- package/dist/types/heatmap.d.ts +0 -5
- package/dist/types/hot-segments.d.ts +0 -9
- package/dist/types/image.d.ts +0 -9
- package/dist/types/uuid.d.ts +0 -1
- package/dist/types/video.d.ts +0 -22
package/dist/lib/hls-setup.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { isHlsStream } from "./hls-utils.js";
|
|
2
|
-
import { playerStateStore } from "../store/player-store.js";
|
|
3
|
-
import { MAX_MEDIA_RECOVERIES, MAX_NETWORK_RETRIES, createHlsErrorRecoveryState, decideHlsErrorAction } from "./hls-error-recovery.js";
|
|
4
|
-
import { loadHls } from "./hls-loader.js";
|
|
5
|
-
import { setupHlsEventHandlers } from "./hls-event-handlers.js";
|
|
6
|
-
import { createSeekingHandler } from "./hls-buffer-flush.js";
|
|
7
|
-
|
|
8
|
-
// src/lib/hls-setup.ts
|
|
9
|
-
function createHlsConfig(isPreloading) {
|
|
10
|
-
return {
|
|
11
|
-
debug: false,
|
|
12
|
-
enableWorker: true,
|
|
13
|
-
lowLatencyMode: false,
|
|
14
|
-
backBufferLength: 10,
|
|
15
|
-
maxBufferLength: isPreloading ? 10 : 30,
|
|
16
|
-
maxMaxBufferLength: isPreloading ? 20 : 60,
|
|
17
|
-
maxBufferSize: 6e7,
|
|
18
|
-
maxBufferHole: .3,
|
|
19
|
-
manifestLoadingTimeOut: 1e4,
|
|
20
|
-
fragLoadingTimeOut: 2e4,
|
|
21
|
-
startFragPrefetch: true,
|
|
22
|
-
highBufferWatchdogPeriod: 2,
|
|
23
|
-
nudgeOffset: .2,
|
|
24
|
-
nudgeMaxRetry: 5,
|
|
25
|
-
testBandwidth: true,
|
|
26
|
-
progressive: false,
|
|
27
|
-
abrEwmaDefaultEstimate: 5e6,
|
|
28
|
-
abrMaxWithRealBitrate: true
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
function tryAttachNativeSource(HlsClass, videoElement, videoUrl) {
|
|
32
|
-
if (!HlsClass.isSupported() && videoElement.canPlayType("application/vnd.apple.mpegurl")) {
|
|
33
|
-
videoElement.src = videoUrl;
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
function bindErrorHandler(hlsInstance, events, onError) {
|
|
39
|
-
const recoveryState = createHlsErrorRecoveryState();
|
|
40
|
-
hlsInstance.on(events.ERROR, (_event, data) => {
|
|
41
|
-
const action = decideHlsErrorAction(data, recoveryState);
|
|
42
|
-
if (!action) return;
|
|
43
|
-
switch (action.kind) {
|
|
44
|
-
case "retry-network":
|
|
45
|
-
hlsInstance.startLoad();
|
|
46
|
-
return;
|
|
47
|
-
case "recover-media":
|
|
48
|
-
hlsInstance.recoverMediaError();
|
|
49
|
-
return;
|
|
50
|
-
case "give-up":
|
|
51
|
-
onError?.(action.payload);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
function buildController(opts) {
|
|
57
|
-
const { hlsInstance, videoElement, playerId, handleSeeking, state } = opts;
|
|
58
|
-
return {
|
|
59
|
-
hls: hlsInstance,
|
|
60
|
-
get levels() {
|
|
61
|
-
return state.levels;
|
|
62
|
-
},
|
|
63
|
-
get currentLevel() {
|
|
64
|
-
return state.currentLevel;
|
|
65
|
-
},
|
|
66
|
-
get isAuto() {
|
|
67
|
-
return state.isAuto;
|
|
68
|
-
},
|
|
69
|
-
setQuality: (index) => {
|
|
70
|
-
if (index === -1) {
|
|
71
|
-
hlsInstance.loadLevel = -1;
|
|
72
|
-
state.isAuto = true;
|
|
73
|
-
} else {
|
|
74
|
-
hlsInstance.loadLevel = index;
|
|
75
|
-
state.isAuto = false;
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
destroy: () => {
|
|
79
|
-
videoElement.removeEventListener("seeking", handleSeeking);
|
|
80
|
-
hlsInstance.destroy();
|
|
81
|
-
playerStateStore.setHlsInstance(playerId, null);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Set up HLS streaming on a video element, resolving to a controller for
|
|
87
|
-
* quality/audio switching and teardown.
|
|
88
|
-
*
|
|
89
|
-
* - For non-HLS URLs: sets `videoElement.src` directly and resolves to `null`
|
|
90
|
-
* without loading hls.js.
|
|
91
|
-
* - For HLS URLs on Safari (no `Hls.isSupported()` but native canPlayType):
|
|
92
|
-
* sets `videoElement.src` and returns `null` (UI features that depend on
|
|
93
|
-
* the HLS controller — quality switching, audio tracks — won't be available
|
|
94
|
-
* in native mode).
|
|
95
|
-
* - Otherwise: instantiates HLS.js, attaches the media, wires events
|
|
96
|
-
* (including error recovery), and returns the controller.
|
|
97
|
-
*/
|
|
98
|
-
async function setupHls(opts) {
|
|
99
|
-
const { videoElement, videoUrl, playerId, isPreloading, onError, onLevelsChange, onLevelSwitch } = opts;
|
|
100
|
-
if (!isHlsStream(videoUrl)) {
|
|
101
|
-
videoElement.src = videoUrl;
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
const HlsClass = await loadHls();
|
|
105
|
-
if (tryAttachNativeSource(HlsClass, videoElement, videoUrl)) return null;
|
|
106
|
-
const hlsInstance = new HlsClass(createHlsConfig(isPreloading));
|
|
107
|
-
const state = {
|
|
108
|
-
levels: [],
|
|
109
|
-
currentLevel: -1,
|
|
110
|
-
isAuto: true
|
|
111
|
-
};
|
|
112
|
-
const handleSeeking = createSeekingHandler({
|
|
113
|
-
hlsInstance,
|
|
114
|
-
events: HlsClass.Events,
|
|
115
|
-
videoElement
|
|
116
|
-
});
|
|
117
|
-
videoElement.addEventListener("seeking", handleSeeking);
|
|
118
|
-
playerStateStore.setHlsInstance(playerId, hlsInstance);
|
|
119
|
-
hlsInstance.attachMedia(videoElement);
|
|
120
|
-
setupHlsEventHandlers({
|
|
121
|
-
hlsInstance,
|
|
122
|
-
events: HlsClass.Events,
|
|
123
|
-
playerId,
|
|
124
|
-
videoUrl,
|
|
125
|
-
onLevelsChange: (levels) => {
|
|
126
|
-
state.levels = levels;
|
|
127
|
-
onLevelsChange?.(levels);
|
|
128
|
-
},
|
|
129
|
-
onLevelSwitch: (level) => {
|
|
130
|
-
state.currentLevel = level;
|
|
131
|
-
onLevelSwitch?.(level);
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
bindErrorHandler(hlsInstance, HlsClass.Events, onError);
|
|
135
|
-
return buildController({
|
|
136
|
-
hlsInstance,
|
|
137
|
-
videoElement,
|
|
138
|
-
playerId,
|
|
139
|
-
handleSeeking,
|
|
140
|
-
state
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export { MAX_MEDIA_RECOVERIES, MAX_NETWORK_RETRIES, createHlsConfig, createHlsErrorRecoveryState, decideHlsErrorAction, setupHls };
|
package/dist/lib/hls-utils.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { MediaPlaylist } from "hls.js";
|
|
2
|
-
export interface QualityLevel {
|
|
3
|
-
height: number;
|
|
4
|
-
width: number;
|
|
5
|
-
bitrate: number;
|
|
6
|
-
name: string;
|
|
7
|
-
index: number;
|
|
8
|
-
}
|
|
9
|
-
export interface AudioTrack {
|
|
10
|
-
index: number;
|
|
11
|
-
label: string;
|
|
12
|
-
language: string;
|
|
13
|
-
kind: string;
|
|
14
|
-
/** The raw Hls.js MediaPlaylist object for this audio track. */
|
|
15
|
-
track: MediaPlaylist;
|
|
16
|
-
}
|
|
17
|
-
export declare const sortLevelsByQuality: (levels: QualityLevel[]) => QualityLevel[];
|
|
18
|
-
export declare const isHlsStream: (url: string) => boolean;
|
|
19
|
-
export declare const generateLevelName: (level: {
|
|
20
|
-
height: number;
|
|
21
|
-
width: number;
|
|
22
|
-
bitrate: number;
|
|
23
|
-
}) => string;
|
|
24
|
-
interface QualityLabelParams {
|
|
25
|
-
resolution: number;
|
|
26
|
-
levels: QualityLevel[];
|
|
27
|
-
currentLevel: number;
|
|
28
|
-
autoText: string;
|
|
29
|
-
}
|
|
30
|
-
export declare const getQualityLabel: ({ resolution, levels, currentLevel, autoText }: QualityLabelParams) => string;
|
|
31
|
-
export {};
|
package/dist/lib/hls-utils.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// src/lib/hls-utils.ts
|
|
2
|
-
const sortLevelsByQuality = (levels) => {
|
|
3
|
-
return [...levels].sort((a, b) => {
|
|
4
|
-
if (a.height > 0 && b.height > 0) return b.height - a.height;
|
|
5
|
-
else if (a.height > 0) return -1;
|
|
6
|
-
else if (b.height > 0) return 1;
|
|
7
|
-
else return b.bitrate - a.bitrate;
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
const isHlsStream = (url) => {
|
|
11
|
-
return url.includes(".m3u8") || url.includes("application/vnd.apple.mpegurl") || url.includes("manifest(format=m3u8") || url.toLowerCase().includes("m3u8");
|
|
12
|
-
};
|
|
13
|
-
const generateLevelName = (level) => {
|
|
14
|
-
if (level.height > 0 && level.width > 0) return `${Math.min(level.height, level.width)}p`;
|
|
15
|
-
else if (level.height > 0) return `${level.height}p`;
|
|
16
|
-
else if (level.width > 0) return `${level.width}p`;
|
|
17
|
-
else {
|
|
18
|
-
const bitrateKbps = Math.round(level.bitrate / 1e3);
|
|
19
|
-
if (bitrateKbps >= 3e3) return "1080p";
|
|
20
|
-
else if (bitrateKbps >= 2e3) return "720p";
|
|
21
|
-
else if (bitrateKbps >= 1e3) return "480p";
|
|
22
|
-
else if (bitrateKbps >= 500) return "360p";
|
|
23
|
-
else if (bitrateKbps >= 250) return "240p";
|
|
24
|
-
else return "144p";
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
const getQualityLabel = ({ resolution, levels, currentLevel, autoText }) => {
|
|
28
|
-
const isAutoSelected = resolution === -1;
|
|
29
|
-
if (isAutoSelected && levels.length > 0 && currentLevel >= 0 && levels[currentLevel]) return `${autoText} (${levels[currentLevel].name})`;
|
|
30
|
-
else if (!isAutoSelected && resolution >= 0 && levels[resolution]) return levels[resolution].name;
|
|
31
|
-
return autoText;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export { generateLevelName, getQualityLabel, isHlsStream, sortLevelsByQuality };
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
export type CSSProperties = Record<string, string | number | null | undefined>;
|
|
2
|
-
export interface ImageRenderState {
|
|
3
|
-
isLoading: boolean;
|
|
4
|
-
isVisible: boolean;
|
|
5
|
-
aspectRatio: number;
|
|
6
|
-
}
|
|
7
|
-
export type ImageRenderAction = {
|
|
8
|
-
type: "reset";
|
|
9
|
-
aspectRatio?: number;
|
|
10
|
-
} | {
|
|
11
|
-
type: "loaded";
|
|
12
|
-
} | {
|
|
13
|
-
type: "visible";
|
|
14
|
-
} | {
|
|
15
|
-
type: "set_aspect_ratio";
|
|
16
|
-
aspectRatio: number;
|
|
17
|
-
};
|
|
18
|
-
export declare const DEFAULT_IMAGE_ASPECT_RATIO = .5;
|
|
19
|
-
export declare const IMAGE_ASPECT_RATIO_THRESHOLD = .7;
|
|
20
|
-
export declare const initialImageRenderState: ImageRenderState;
|
|
21
|
-
export interface ImageContainerStyleParams {
|
|
22
|
-
aspectRatio: number;
|
|
23
|
-
isVisible: boolean;
|
|
24
|
-
thumbhashUrl?: string;
|
|
25
|
-
}
|
|
26
|
-
export declare function imageRenderReducer(state: ImageRenderState, action: ImageRenderAction): ImageRenderState;
|
|
27
|
-
export declare function getImageAspectRatio(width?: number, height?: number): number | undefined;
|
|
28
|
-
export declare function getImageContainerStyle({ aspectRatio, isVisible, thumbhashUrl }: ImageContainerStyleParams): CSSProperties;
|
|
29
|
-
export declare function getImageStyle(aspectRatio: number, isLoading: boolean): CSSProperties;
|
package/dist/lib/image-render.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
// src/lib/image-render.ts
|
|
2
|
-
const DEFAULT_IMAGE_ASPECT_RATIO = .5;
|
|
3
|
-
const IMAGE_ASPECT_RATIO_THRESHOLD = .7;
|
|
4
|
-
const initialImageRenderState = {
|
|
5
|
-
isLoading: true,
|
|
6
|
-
isVisible: false,
|
|
7
|
-
aspectRatio: DEFAULT_IMAGE_ASPECT_RATIO
|
|
8
|
-
};
|
|
9
|
-
function imageRenderReducer(state, action) {
|
|
10
|
-
switch (action.type) {
|
|
11
|
-
case "reset": return {
|
|
12
|
-
isLoading: true,
|
|
13
|
-
isVisible: false,
|
|
14
|
-
aspectRatio: action.aspectRatio ?? state.aspectRatio
|
|
15
|
-
};
|
|
16
|
-
case "loaded": return {
|
|
17
|
-
...state,
|
|
18
|
-
isLoading: false
|
|
19
|
-
};
|
|
20
|
-
case "visible": return {
|
|
21
|
-
...state,
|
|
22
|
-
isVisible: true
|
|
23
|
-
};
|
|
24
|
-
case "set_aspect_ratio": return {
|
|
25
|
-
...state,
|
|
26
|
-
aspectRatio: action.aspectRatio
|
|
27
|
-
};
|
|
28
|
-
default: return state;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function getImageAspectRatio(width, height) {
|
|
32
|
-
if (!width || !height) return void 0;
|
|
33
|
-
return width / height;
|
|
34
|
-
}
|
|
35
|
-
function getImageContainerStyle({ aspectRatio, isVisible, thumbhashUrl }) {
|
|
36
|
-
const style = {
|
|
37
|
-
display: "block",
|
|
38
|
-
width: "100%",
|
|
39
|
-
aspectRatio,
|
|
40
|
-
position: "relative"
|
|
41
|
-
};
|
|
42
|
-
if (!isVisible) {
|
|
43
|
-
style.backgroundImage = `url(${thumbhashUrl})`;
|
|
44
|
-
style.backgroundSize = aspectRatio >= .7 ? "contain" : "cover";
|
|
45
|
-
style.backgroundPosition = "center";
|
|
46
|
-
style.backgroundRepeat = "no-repeat";
|
|
47
|
-
}
|
|
48
|
-
return style;
|
|
49
|
-
}
|
|
50
|
-
function getImageStyle(aspectRatio, isLoading) {
|
|
51
|
-
return {
|
|
52
|
-
position: "absolute",
|
|
53
|
-
top: 0,
|
|
54
|
-
left: 0,
|
|
55
|
-
width: "100%",
|
|
56
|
-
height: "100%",
|
|
57
|
-
objectFit: aspectRatio >= .7 ? "contain" : "cover",
|
|
58
|
-
opacity: isLoading ? 0 : 1,
|
|
59
|
-
transition: "opacity 300ms ease"
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export { DEFAULT_IMAGE_ASPECT_RATIO, IMAGE_ASPECT_RATIO_THRESHOLD, getImageAspectRatio, getImageContainerStyle, getImageStyle, imageRenderReducer, initialImageRenderState };
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Media SDK video keyboard shortcuts configuration
|
|
3
|
-
*/
|
|
4
|
-
export type VideoSdkKey = "Space" | "KeyK" | "KeyJ" | "KeyL" | "KeyI" | "KeyF" | "KeyM" | "KeyN" | "KeyP" | "ArrowUp" | "ArrowDown" | "ArrowLeft" | "ArrowRight" | "MediaPlay" | "MediaPause" | "MediaPlayPause" | "F1" | "F12" | "Digit0" | "Digit1" | "Digit2" | "Digit3" | "Digit4" | "Digit5" | "Digit6" | "Digit7" | "Digit8" | "Digit9";
|
|
5
|
-
export declare const ShortcutAction: {
|
|
6
|
-
readonly PLAY_PAUSE: "play_pause";
|
|
7
|
-
readonly SEEK_10: "seek_10";
|
|
8
|
-
readonly SEEK_5: "seek_5";
|
|
9
|
-
readonly FULLSCREEN: "fullscreen";
|
|
10
|
-
readonly MINIPLAYER: "miniplayer";
|
|
11
|
-
readonly MUTE: "mute";
|
|
12
|
-
readonly VOLUME: "volume";
|
|
13
|
-
};
|
|
14
|
-
export type ShortcutAction = (typeof ShortcutAction)[keyof typeof ShortcutAction];
|
|
15
|
-
export interface ShortcutDefinition {
|
|
16
|
-
action: ShortcutAction;
|
|
17
|
-
keys: VideoSdkKey[];
|
|
18
|
-
labelKey: string;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Default keyboard shortcuts configuration
|
|
22
|
-
*/
|
|
23
|
-
export declare const SHORTCUTS_CONFIG: ShortcutDefinition[];
|
|
24
|
-
/**
|
|
25
|
-
* Seek amounts in seconds
|
|
26
|
-
*/
|
|
27
|
-
export declare const SEEK_SMALL_SECONDS = 5;
|
|
28
|
-
export declare const SEEK_LARGE_SECONDS = 10;
|
|
29
|
-
/**
|
|
30
|
-
* Volume change delta (0-1 range)
|
|
31
|
-
*/
|
|
32
|
-
export declare const VOLUME_DELTA = .1;
|
|
33
|
-
export interface VideoKeyboardHandlers {
|
|
34
|
-
onPlayPause: () => void;
|
|
35
|
-
onSeek: (seconds: number) => void;
|
|
36
|
-
onFullscreen: () => void;
|
|
37
|
-
onMiniplayer: () => void;
|
|
38
|
-
onMute: () => void;
|
|
39
|
-
onVolume: (delta: number) => void;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Get seek amount for a keyboard event
|
|
43
|
-
* @returns number of seconds to seek (positive = forward, negative = backward)
|
|
44
|
-
*/
|
|
45
|
-
export declare function getSeekAmount(keyCode: string, action: ShortcutAction): number;
|
|
46
|
-
/**
|
|
47
|
-
* Get volume delta for a keyboard event
|
|
48
|
-
* @returns volume change amount (positive = increase, negative = decrease)
|
|
49
|
-
*/
|
|
50
|
-
export declare function getVolumeDelta(keyCode: string): number;
|
|
51
|
-
/**
|
|
52
|
-
* Execute keyboard shortcut action
|
|
53
|
-
*/
|
|
54
|
-
export declare function executeShortcutAction(action: ShortcutAction, keyCode: string, handlers: VideoKeyboardHandlers): void;
|
|
55
|
-
/**
|
|
56
|
-
* Full play/pause key set including media keys — mirrors React's
|
|
57
|
-
* component-level PlayPauseShortcuts, which extends SHORTCUTS_CONFIG.
|
|
58
|
-
*/
|
|
59
|
-
export declare const PLAY_PAUSE_KEYS: VideoSdkKey[];
|
|
60
|
-
/** Resolve a KeyboardEvent.code to its shortcut action, or null when unmapped. */
|
|
61
|
-
export declare function resolveShortcutAction(keyCode: string): ShortcutAction | null;
|
|
62
|
-
/**
|
|
63
|
-
* Digit0–Digit9 → 0–90 percent seek targets — mirrors React's
|
|
64
|
-
* PercentageShortcuts table.
|
|
65
|
-
*/
|
|
66
|
-
export declare function getSeekPercent(keyCode: string): number | null;
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
// src/lib/keyboard-config.ts
|
|
2
|
-
const ShortcutAction = {
|
|
3
|
-
PLAY_PAUSE: "play_pause",
|
|
4
|
-
SEEK_10: "seek_10",
|
|
5
|
-
SEEK_5: "seek_5",
|
|
6
|
-
FULLSCREEN: "fullscreen",
|
|
7
|
-
MINIPLAYER: "miniplayer",
|
|
8
|
-
MUTE: "mute",
|
|
9
|
-
VOLUME: "volume"
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* Default keyboard shortcuts configuration
|
|
13
|
-
*/
|
|
14
|
-
const SHORTCUTS_CONFIG = [
|
|
15
|
-
{
|
|
16
|
-
action: ShortcutAction.PLAY_PAUSE,
|
|
17
|
-
keys: ["Space", "KeyK"],
|
|
18
|
-
labelKey: "shortcuts.play_pause"
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
action: ShortcutAction.SEEK_10,
|
|
22
|
-
keys: ["KeyJ", "KeyL"],
|
|
23
|
-
labelKey: "shortcuts.seek_10s"
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
action: ShortcutAction.SEEK_5,
|
|
27
|
-
keys: ["ArrowLeft", "ArrowRight"],
|
|
28
|
-
labelKey: "shortcuts.seek_5s"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
action: ShortcutAction.FULLSCREEN,
|
|
32
|
-
keys: ["KeyF"],
|
|
33
|
-
labelKey: "shortcuts.fullscreen"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
action: ShortcutAction.MINIPLAYER,
|
|
37
|
-
keys: ["KeyI"],
|
|
38
|
-
labelKey: "shortcuts.pip"
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
action: ShortcutAction.MUTE,
|
|
42
|
-
keys: ["KeyM"],
|
|
43
|
-
labelKey: "shortcuts.mute"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
action: ShortcutAction.VOLUME,
|
|
47
|
-
keys: ["ArrowUp", "ArrowDown"],
|
|
48
|
-
labelKey: "shortcuts.volume"
|
|
49
|
-
}
|
|
50
|
-
];
|
|
51
|
-
/**
|
|
52
|
-
* Seek amounts in seconds
|
|
53
|
-
*/
|
|
54
|
-
const SEEK_SMALL_SECONDS = 5;
|
|
55
|
-
const SEEK_LARGE_SECONDS = 10;
|
|
56
|
-
/**
|
|
57
|
-
* Volume change delta (0-1 range)
|
|
58
|
-
*/
|
|
59
|
-
const VOLUME_DELTA = .1;
|
|
60
|
-
/**
|
|
61
|
-
* Get seek amount for a keyboard event
|
|
62
|
-
* @returns number of seconds to seek (positive = forward, negative = backward)
|
|
63
|
-
*/
|
|
64
|
-
function getSeekAmount(keyCode, action) {
|
|
65
|
-
if (action === ShortcutAction.SEEK_10) return keyCode === "KeyL" ? 10 : -10;
|
|
66
|
-
if (action === ShortcutAction.SEEK_5) return keyCode === "ArrowRight" ? 5 : -5;
|
|
67
|
-
return 0;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Get volume delta for a keyboard event
|
|
71
|
-
* @returns volume change amount (positive = increase, negative = decrease)
|
|
72
|
-
*/
|
|
73
|
-
function getVolumeDelta(keyCode) {
|
|
74
|
-
return keyCode === "ArrowUp" ? VOLUME_DELTA : -.1;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Execute keyboard shortcut action
|
|
78
|
-
*/
|
|
79
|
-
function executeShortcutAction(action, keyCode, handlers) {
|
|
80
|
-
switch (action) {
|
|
81
|
-
case ShortcutAction.PLAY_PAUSE:
|
|
82
|
-
handlers.onPlayPause();
|
|
83
|
-
break;
|
|
84
|
-
case ShortcutAction.SEEK_10:
|
|
85
|
-
case ShortcutAction.SEEK_5:
|
|
86
|
-
handlers.onSeek(getSeekAmount(keyCode, action));
|
|
87
|
-
break;
|
|
88
|
-
case ShortcutAction.FULLSCREEN:
|
|
89
|
-
handlers.onFullscreen();
|
|
90
|
-
break;
|
|
91
|
-
case ShortcutAction.MINIPLAYER:
|
|
92
|
-
handlers.onMiniplayer();
|
|
93
|
-
break;
|
|
94
|
-
case ShortcutAction.MUTE:
|
|
95
|
-
handlers.onMute();
|
|
96
|
-
break;
|
|
97
|
-
case ShortcutAction.VOLUME: handlers.onVolume(getVolumeDelta(keyCode));
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Full play/pause key set including media keys — mirrors React's
|
|
102
|
-
* component-level PlayPauseShortcuts, which extends SHORTCUTS_CONFIG.
|
|
103
|
-
*/
|
|
104
|
-
const PLAY_PAUSE_KEYS = [
|
|
105
|
-
"Space",
|
|
106
|
-
"KeyK",
|
|
107
|
-
"F1",
|
|
108
|
-
"F12",
|
|
109
|
-
"MediaPlay",
|
|
110
|
-
"MediaPause",
|
|
111
|
-
"MediaPlayPause"
|
|
112
|
-
];
|
|
113
|
-
const KEY_ACTION_MAP = new Map([...SHORTCUTS_CONFIG.flatMap((config) => config.keys.map((key) => [key, config.action])), ...PLAY_PAUSE_KEYS.map((key) => [key, ShortcutAction.PLAY_PAUSE])]);
|
|
114
|
-
/** Resolve a KeyboardEvent.code to its shortcut action, or null when unmapped. */
|
|
115
|
-
function resolveShortcutAction(keyCode) {
|
|
116
|
-
return KEY_ACTION_MAP.get(keyCode) ?? null;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Digit0–Digit9 → 0–90 percent seek targets — mirrors React's
|
|
120
|
-
* PercentageShortcuts table.
|
|
121
|
-
*/
|
|
122
|
-
function getSeekPercent(keyCode) {
|
|
123
|
-
if (!/^Digit[0-9]$/.test(keyCode)) return null;
|
|
124
|
-
return Number(keyCode.slice(5)) * 10;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export { PLAY_PAUSE_KEYS, SEEK_LARGE_SECONDS, SEEK_SMALL_SECONDS, SHORTCUTS_CONFIG, ShortcutAction, VOLUME_DELTA, executeShortcutAction, getSeekAmount, getSeekPercent, getVolumeDelta, resolveShortcutAction };
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// src/lib/keyboard-utils.ts
|
|
2
|
-
/**
|
|
3
|
-
* Check if keyboard event should be ignored (input fields, textareas, etc.)
|
|
4
|
-
*/
|
|
5
|
-
function shouldIgnoreKeyboardEvent(target) {
|
|
6
|
-
if (!target || !(target instanceof HTMLElement)) return false;
|
|
7
|
-
const tagName = target.tagName;
|
|
8
|
-
return tagName === "INPUT" || tagName === "TEXTAREA" || target.contentEditable === "true";
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export { shouldIgnoreKeyboardEvent };
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { VideoLoadState } from "./video-preload-config.js";
|
|
2
|
-
/**
|
|
3
|
-
* Pure helpers and DOM-listener factories for player lifecycle effects:
|
|
4
|
-
* playback rate sync, ended handling, preload state machine, reset-on-data
|
|
5
|
-
* derivations, and feed playback gating.
|
|
6
|
-
*
|
|
7
|
-
* Framework-agnostic. React/Lit/Vue wrappers supply the store-update callbacks;
|
|
8
|
-
* this module supplies the predicates, derivations, and listener factories.
|
|
9
|
-
*/
|
|
10
|
-
/** Valid values for `<video preload>`. */
|
|
11
|
-
export type VideoPreloadAttribute = "auto" | "metadata" | "none";
|
|
12
|
-
/**
|
|
13
|
-
* Map a feed virtualization `VideoLoadState` to the appropriate
|
|
14
|
-
* `<video preload>` attribute value.
|
|
15
|
-
*
|
|
16
|
-
* - UNLOADED → "none" (slot is far from viewport; don't fetch anything)
|
|
17
|
-
* - IDLE → "metadata" (slot is reachable but not active; fetch headers only)
|
|
18
|
-
* - PRELOADING / PLAYING → "auto" (slot is active or about to be; fetch segments)
|
|
19
|
-
*/
|
|
20
|
-
export declare function getPreloadAttribute(loadState: VideoLoadState): VideoPreloadAttribute;
|
|
21
|
-
/**
|
|
22
|
-
* Bind a `loadeddata` listener that re-applies the playback rate after the
|
|
23
|
-
* browser resets it (most browsers reset `playbackRate` to `1.0` on each
|
|
24
|
-
* `<source>` change).
|
|
25
|
-
*
|
|
26
|
-
* Also writes the rate immediately so that callers don't have to do so
|
|
27
|
-
* separately. Returns a teardown function.
|
|
28
|
-
*/
|
|
29
|
-
export declare function bindPlaybackRateSync(videoElement: HTMLVideoElement | null, playbackRate: number): () => void;
|
|
30
|
-
/**
|
|
31
|
-
* Bind an `ended` listener that invokes `onEnded()` exactly once per native
|
|
32
|
-
* `ended` event. Returns a teardown function.
|
|
33
|
-
*/
|
|
34
|
-
export declare function bindEndedListener(videoElement: HTMLVideoElement | null, onEnded: () => void): () => void;
|
|
35
|
-
export interface PosterDimensions {
|
|
36
|
-
width?: number;
|
|
37
|
-
height?: number;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Compute the aspect ratio (width / height) from poster dimensions, returning
|
|
41
|
-
* `null` when either dimension is missing or zero.
|
|
42
|
-
*
|
|
43
|
-
* Mirrors the conditional in `useResetOnDataChangeEffect`:
|
|
44
|
-
*
|
|
45
|
-
* if (videoData?.poster?.width && videoData?.poster?.height) {
|
|
46
|
-
* setAspectRatio(videoData.poster.width / videoData.poster.height);
|
|
47
|
-
* }
|
|
48
|
-
*/
|
|
49
|
-
export declare function getAspectRatioFromPoster(poster: PosterDimensions | undefined): number | null;
|
|
50
|
-
export interface FeedPlaybackDecisionInput {
|
|
51
|
-
isFeed: boolean;
|
|
52
|
-
isCurrent: boolean;
|
|
53
|
-
loadState: VideoLoadState;
|
|
54
|
-
showSpinner: boolean;
|
|
55
|
-
videoReadyState: number;
|
|
56
|
-
}
|
|
57
|
-
export type FeedPlaybackAction = "play" | "pause" | "none";
|
|
58
|
-
/**
|
|
59
|
-
* Decide what a feed-virtualized player should do given the current slot state.
|
|
60
|
-
*
|
|
61
|
-
* - In a non-feed context, returns "none" (caller handles its own playback).
|
|
62
|
-
* - If the slot is not current, returns "pause" so the off-screen player stops.
|
|
63
|
-
* - If the slot is current, in PLAYING load state, not spinning, and the video
|
|
64
|
-
* has enough data (readyState >= 2), returns "play".
|
|
65
|
-
* - Otherwise returns "none" (still loading / preloading; wait).
|
|
66
|
-
*/
|
|
67
|
-
export declare function getFeedPlaybackAction({ isFeed, isCurrent, loadState, showSpinner, videoReadyState }: FeedPlaybackDecisionInput): FeedPlaybackAction;
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { VideoLoadState } from "./video-preload-config.js";
|
|
2
|
-
|
|
3
|
-
// src/lib/lifecycle-effects.ts
|
|
4
|
-
/**
|
|
5
|
-
* Map a feed virtualization `VideoLoadState` to the appropriate
|
|
6
|
-
* `<video preload>` attribute value.
|
|
7
|
-
*
|
|
8
|
-
* - UNLOADED → "none" (slot is far from viewport; don't fetch anything)
|
|
9
|
-
* - IDLE → "metadata" (slot is reachable but not active; fetch headers only)
|
|
10
|
-
* - PRELOADING / PLAYING → "auto" (slot is active or about to be; fetch segments)
|
|
11
|
-
*/
|
|
12
|
-
function getPreloadAttribute(loadState) {
|
|
13
|
-
switch (loadState) {
|
|
14
|
-
case VideoLoadState.UNLOADED: return "none";
|
|
15
|
-
case VideoLoadState.IDLE: return "metadata";
|
|
16
|
-
case VideoLoadState.PRELOADING:
|
|
17
|
-
case VideoLoadState.PLAYING: return "auto";
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Bind a `loadeddata` listener that re-applies the playback rate after the
|
|
22
|
-
* browser resets it (most browsers reset `playbackRate` to `1.0` on each
|
|
23
|
-
* `<source>` change).
|
|
24
|
-
*
|
|
25
|
-
* Also writes the rate immediately so that callers don't have to do so
|
|
26
|
-
* separately. Returns a teardown function.
|
|
27
|
-
*/
|
|
28
|
-
function bindPlaybackRateSync(videoElement, playbackRate) {
|
|
29
|
-
if (!videoElement) return () => void 0;
|
|
30
|
-
videoElement.playbackRate = playbackRate;
|
|
31
|
-
const handler = () => {
|
|
32
|
-
videoElement.playbackRate = playbackRate;
|
|
33
|
-
};
|
|
34
|
-
videoElement.addEventListener("loadeddata", handler);
|
|
35
|
-
return () => videoElement.removeEventListener("loadeddata", handler);
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Bind an `ended` listener that invokes `onEnded()` exactly once per native
|
|
39
|
-
* `ended` event. Returns a teardown function.
|
|
40
|
-
*/
|
|
41
|
-
function bindEndedListener(videoElement, onEnded) {
|
|
42
|
-
if (!videoElement) return () => void 0;
|
|
43
|
-
videoElement.addEventListener("ended", onEnded);
|
|
44
|
-
return () => videoElement.removeEventListener("ended", onEnded);
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Compute the aspect ratio (width / height) from poster dimensions, returning
|
|
48
|
-
* `null` when either dimension is missing or zero.
|
|
49
|
-
*
|
|
50
|
-
* Mirrors the conditional in `useResetOnDataChangeEffect`:
|
|
51
|
-
*
|
|
52
|
-
* if (videoData?.poster?.width && videoData?.poster?.height) {
|
|
53
|
-
* setAspectRatio(videoData.poster.width / videoData.poster.height);
|
|
54
|
-
* }
|
|
55
|
-
*/
|
|
56
|
-
function getAspectRatioFromPoster(poster) {
|
|
57
|
-
if (!poster?.width || !poster?.height) return null;
|
|
58
|
-
return poster.width / poster.height;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Decide what a feed-virtualized player should do given the current slot state.
|
|
62
|
-
*
|
|
63
|
-
* - In a non-feed context, returns "none" (caller handles its own playback).
|
|
64
|
-
* - If the slot is not current, returns "pause" so the off-screen player stops.
|
|
65
|
-
* - If the slot is current, in PLAYING load state, not spinning, and the video
|
|
66
|
-
* has enough data (readyState >= 2), returns "play".
|
|
67
|
-
* - Otherwise returns "none" (still loading / preloading; wait).
|
|
68
|
-
*/
|
|
69
|
-
function getFeedPlaybackAction({ isFeed, isCurrent, loadState, showSpinner, videoReadyState }) {
|
|
70
|
-
if (!isFeed) return "none";
|
|
71
|
-
if (!isCurrent) return "pause";
|
|
72
|
-
if (loadState !== VideoLoadState.PLAYING) return "none";
|
|
73
|
-
if (showSpinner) return "none";
|
|
74
|
-
if (videoReadyState < 2) return "none";
|
|
75
|
-
return "play";
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export { bindEndedListener, bindPlaybackRateSync, getAspectRatioFromPoster, getFeedPlaybackAction, getPreloadAttribute };
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Delay in milliseconds before showing the loading spinner
|
|
3
|
-
* This prevents flash-of-spinner for brief buffering moments
|
|
4
|
-
*/
|
|
5
|
-
export declare const LOADING_SPINNER_DELAY_MS = 300;
|
|
6
|
-
export interface LoadingDebounceState {
|
|
7
|
-
timerId: ReturnType<typeof setTimeout> | null;
|
|
8
|
-
}
|
|
9
|
-
export interface UpdateLoadingStateOptions {
|
|
10
|
-
state: LoadingDebounceState;
|
|
11
|
-
shouldShow: boolean;
|
|
12
|
-
onShow: () => void;
|
|
13
|
-
onHide: () => void;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Create initial loading debounce state
|
|
17
|
-
*/
|
|
18
|
-
export declare function createLoadingDebounceState(): LoadingDebounceState;
|
|
19
|
-
/**
|
|
20
|
-
* Update loading state with debounce
|
|
21
|
-
*/
|
|
22
|
-
export declare function updateLoadingState(options: UpdateLoadingStateOptions): void;
|
|
23
|
-
/**
|
|
24
|
-
* Clean up loading debounce state (clear any pending timers)
|
|
25
|
-
*/
|
|
26
|
-
export declare function cleanupLoadingDebounce(state: LoadingDebounceState): void;
|