@juspay/svelte-ui-components 2.133.1 → 2.134.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.
- package/README.md +30 -0
- package/dist/Draggable/Draggable.svelte +175 -0
- package/dist/Draggable/Draggable.svelte.d.ts +4 -0
- package/dist/Draggable/properties.d.ts +26 -0
- package/dist/Draggable/properties.js +1 -0
- package/dist/Gallery/Gallery.svelte +593 -0
- package/dist/Gallery/Gallery.svelte.d.ts +4 -0
- package/dist/Gallery/properties.d.ts +39 -0
- package/dist/Gallery/properties.js +1 -0
- package/dist/MediaPlayer/MediaPlayer.svelte +277 -0
- package/dist/MediaPlayer/MediaPlayer.svelte.d.ts +4 -0
- package/dist/MediaPlayer/properties.d.ts +34 -0
- package/dist/MediaPlayer/properties.js +1 -0
- package/dist/MediaUpload/MediaUpload.svelte +528 -0
- package/dist/MediaUpload/MediaUpload.svelte.d.ts +4 -0
- package/dist/MediaUpload/properties.d.ts +46 -0
- package/dist/MediaUpload/properties.js +1 -0
- package/dist/Modal/Modal.svelte +17 -3
- package/dist/Modal/properties.d.ts +4 -0
- package/dist/assets/add.svg +4 -0
- package/dist/assets/delete.svg +7 -0
- package/dist/assets/edit.svg +4 -0
- package/dist/assets/file.svg +4 -0
- package/dist/assets/mute.svg +5 -0
- package/dist/assets/pause.svg +4 -0
- package/dist/assets/play.svg +3 -0
- package/dist/assets/volume.svg +5 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +4 -0
- package/dist-wc/index.d.ts +1 -0
- package/dist-wc/index.js +33018 -0
- package/package.json +16 -9
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Button from '../Button/Button.svelte';
|
|
3
|
+
import Img from '../Img/Img.svelte';
|
|
4
|
+
import playSvg from '../assets/play.svg?raw';
|
|
5
|
+
import pauseSvg from '../assets/pause.svg?raw';
|
|
6
|
+
import volumeSvg from '../assets/volume.svg?raw';
|
|
7
|
+
import muteSvg from '../assets/mute.svg?raw';
|
|
8
|
+
import type { MediaPlayerProperties } from './properties';
|
|
9
|
+
|
|
10
|
+
let {
|
|
11
|
+
src,
|
|
12
|
+
type,
|
|
13
|
+
alt = '',
|
|
14
|
+
autoplay = true,
|
|
15
|
+
loop = false,
|
|
16
|
+
controls = false,
|
|
17
|
+
fallback,
|
|
18
|
+
playing = $bindable(true),
|
|
19
|
+
muted = $bindable(true),
|
|
20
|
+
playIcon,
|
|
21
|
+
pauseIcon,
|
|
22
|
+
muteIcon,
|
|
23
|
+
unmuteIcon,
|
|
24
|
+
captionsSrc,
|
|
25
|
+
captionsLabel,
|
|
26
|
+
captionsSrcLang,
|
|
27
|
+
onplay,
|
|
28
|
+
onpause,
|
|
29
|
+
onvolumechange,
|
|
30
|
+
testId,
|
|
31
|
+
classes
|
|
32
|
+
}: MediaPlayerProperties = $props();
|
|
33
|
+
|
|
34
|
+
let videoPlayer: HTMLVideoElement | null = $state(null);
|
|
35
|
+
|
|
36
|
+
function togglePlayback(): void {
|
|
37
|
+
if (videoPlayer === null) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (videoPlayer.paused) {
|
|
41
|
+
videoPlayer.play();
|
|
42
|
+
} else {
|
|
43
|
+
videoPlayer.pause();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// `playing` is bindable so a host can drive playback externally (e.g. pausing this
|
|
48
|
+
// player when another one starts), but the native onplay/onpause handlers below only
|
|
49
|
+
// ever WRITE to it (video state -> playing). Without this, a host setting playing
|
|
50
|
+
// itself had no effect on the actual video at all. Guarded so it only imperatively
|
|
51
|
+
// calls play()/pause() when the DOM element's real state actually disagrees with
|
|
52
|
+
// `playing` -- the native events settle it back in sync afterwards, so this can't loop.
|
|
53
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
54
|
+
$effect(() => {
|
|
55
|
+
if (videoPlayer === null) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (playing && videoPlayer.paused) {
|
|
59
|
+
videoPlayer.play();
|
|
60
|
+
} else if (!playing && !videoPlayer.paused) {
|
|
61
|
+
videoPlayer.pause();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
function toggleMute(): void {
|
|
66
|
+
muted = !muted;
|
|
67
|
+
onvolumechange?.(muted);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function handlePlay(event: Event): void {
|
|
71
|
+
playing = true;
|
|
72
|
+
onplay?.(event);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function handlePause(event: Event): void {
|
|
76
|
+
playing = false;
|
|
77
|
+
onpause?.(event);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function handleVideoKeydown(event: KeyboardEvent): void {
|
|
81
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
togglePlayback();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<div class="media-player {classes ?? ''}" data-pw={typeof testId === 'string' ? testId : null}>
|
|
89
|
+
{#if type === 'image'}
|
|
90
|
+
<span class="media-image">
|
|
91
|
+
<Img {src} {alt} {fallback} />
|
|
92
|
+
</span>
|
|
93
|
+
{:else}
|
|
94
|
+
<video
|
|
95
|
+
bind:this={videoPlayer}
|
|
96
|
+
bind:muted
|
|
97
|
+
{src}
|
|
98
|
+
class="media"
|
|
99
|
+
{controls}
|
|
100
|
+
{autoplay}
|
|
101
|
+
{loop}
|
|
102
|
+
playsinline
|
|
103
|
+
onplay={handlePlay}
|
|
104
|
+
onpause={handlePause}
|
|
105
|
+
onclick={controls ? null : togglePlayback}
|
|
106
|
+
onkeydown={controls ? null : handleVideoKeydown}
|
|
107
|
+
role={controls ? null : 'button'}
|
|
108
|
+
tabindex={controls ? null : 0}
|
|
109
|
+
aria-label={controls ? null : playing ? 'Pause video' : 'Play video'}
|
|
110
|
+
>
|
|
111
|
+
{#if typeof captionsSrc === 'string' && captionsSrc.length > 0}
|
|
112
|
+
<track
|
|
113
|
+
kind="captions"
|
|
114
|
+
src={captionsSrc}
|
|
115
|
+
label={typeof captionsLabel === 'string' ? captionsLabel : null}
|
|
116
|
+
srclang={typeof captionsSrcLang === 'string' ? captionsSrcLang : null}
|
|
117
|
+
/>
|
|
118
|
+
{/if}
|
|
119
|
+
</video>
|
|
120
|
+
|
|
121
|
+
{#if !controls}
|
|
122
|
+
<div class="overlay">
|
|
123
|
+
<div class="center-controls">
|
|
124
|
+
<div class="control center-control">
|
|
125
|
+
<Button onclick={togglePlayback} ariaLabel={playing ? 'Pause video' : 'Play video'}>
|
|
126
|
+
{#if playing}
|
|
127
|
+
{#if typeof pauseIcon === 'function'}
|
|
128
|
+
{@render pauseIcon()}
|
|
129
|
+
{:else}
|
|
130
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
131
|
+
{@html pauseSvg}
|
|
132
|
+
{/if}
|
|
133
|
+
{:else if typeof playIcon === 'function'}
|
|
134
|
+
{@render playIcon()}
|
|
135
|
+
{:else}
|
|
136
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
137
|
+
{@html playSvg}
|
|
138
|
+
{/if}
|
|
139
|
+
</Button>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
<div class="bottom-controls">
|
|
143
|
+
<div class="control bottom-control">
|
|
144
|
+
<Button onclick={toggleMute} ariaLabel={muted ? 'Unmute' : 'Mute'}>
|
|
145
|
+
{#if muted}
|
|
146
|
+
{#if typeof muteIcon === 'function'}
|
|
147
|
+
{@render muteIcon()}
|
|
148
|
+
{:else}
|
|
149
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
150
|
+
{@html muteSvg}
|
|
151
|
+
{/if}
|
|
152
|
+
{:else if typeof unmuteIcon === 'function'}
|
|
153
|
+
{@render unmuteIcon()}
|
|
154
|
+
{:else}
|
|
155
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
156
|
+
{@html volumeSvg}
|
|
157
|
+
{/if}
|
|
158
|
+
</Button>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
{/if}
|
|
163
|
+
{/if}
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<style>
|
|
167
|
+
.media-player {
|
|
168
|
+
box-sizing: border-box;
|
|
169
|
+
display: flex;
|
|
170
|
+
position: relative;
|
|
171
|
+
height: var(--media-player-height, 400px);
|
|
172
|
+
width: var(--media-player-width, fit-content);
|
|
173
|
+
border-radius: var(--media-player-border-radius, 14px);
|
|
174
|
+
overflow: var(--media-player-overflow, hidden);
|
|
175
|
+
background: var(--media-player-background, transparent);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.media {
|
|
179
|
+
height: var(--media-player-media-height, 100%);
|
|
180
|
+
width: var(--media-player-media-width, fit-content);
|
|
181
|
+
object-fit: var(--media-player-media-object-fit, contain);
|
|
182
|
+
border-radius: var(--media-player-media-border-radius, inherit);
|
|
183
|
+
display: block;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
video.media {
|
|
187
|
+
cursor: var(--media-player-media-cursor, pointer);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.media-image {
|
|
191
|
+
display: contents;
|
|
192
|
+
--image-height: var(--media-player-media-height, 100%);
|
|
193
|
+
--image-width: var(--media-player-media-width, fit-content);
|
|
194
|
+
--image-object-fit: var(--media-player-media-object-fit, contain);
|
|
195
|
+
--image-border-radius: var(--media-player-media-border-radius, 0px);
|
|
196
|
+
--image-padding: 0px;
|
|
197
|
+
--image-margin: 0px;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.overlay {
|
|
201
|
+
position: absolute;
|
|
202
|
+
inset: 0;
|
|
203
|
+
display: flex;
|
|
204
|
+
flex-direction: column;
|
|
205
|
+
justify-content: center;
|
|
206
|
+
align-items: center;
|
|
207
|
+
box-sizing: border-box;
|
|
208
|
+
z-index: var(--media-player-overlay-z-index, 20);
|
|
209
|
+
border-radius: inherit;
|
|
210
|
+
background-color: var(--media-player-overlay-color, transparent);
|
|
211
|
+
transition: var(--media-player-overlay-transition, background-color 0.2s ease);
|
|
212
|
+
--center-controls-visibility: var(--media-player-center-controls-visibility, hidden);
|
|
213
|
+
--bottom-controls-visibility: var(--media-player-bottom-controls-visibility, hidden);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* :focus-within alongside :hover so the overlay controls (play/pause, mute) are
|
|
217
|
+
reachable for keyboard users too -- :hover alone left them visibility:hidden (and
|
|
218
|
+
so out of the tab order) for anyone not using a mouse. */
|
|
219
|
+
.overlay:hover,
|
|
220
|
+
.overlay:focus-within {
|
|
221
|
+
background-color: var(--media-player-overlay-hover-color, #0000004d);
|
|
222
|
+
--center-controls-visibility: visible;
|
|
223
|
+
--bottom-controls-visibility: visible;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.center-controls {
|
|
227
|
+
display: flex;
|
|
228
|
+
flex: 1;
|
|
229
|
+
justify-content: center;
|
|
230
|
+
align-items: center;
|
|
231
|
+
visibility: var(--center-controls-visibility);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.bottom-controls {
|
|
235
|
+
display: flex;
|
|
236
|
+
width: 100%;
|
|
237
|
+
height: fit-content;
|
|
238
|
+
justify-content: var(--media-player-bottom-controls-justify, flex-end);
|
|
239
|
+
box-sizing: border-box;
|
|
240
|
+
padding: var(--media-player-bottom-controls-padding, 12px);
|
|
241
|
+
visibility: var(--bottom-controls-visibility);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.control {
|
|
245
|
+
--button-padding: var(--media-player-control-padding, 0px);
|
|
246
|
+
--button-border: var(--media-player-control-border, none);
|
|
247
|
+
--button-border-radius: var(--media-player-control-border-radius, 50%);
|
|
248
|
+
--button-color: var(--media-player-control-background-color, transparent);
|
|
249
|
+
--button-text-color: var(--media-player-control-color, #ffffff);
|
|
250
|
+
--button-content-gap: 0px;
|
|
251
|
+
--button-hover-color: var(
|
|
252
|
+
--media-player-control-hover-background-color,
|
|
253
|
+
var(--media-player-control-background-color, transparent)
|
|
254
|
+
);
|
|
255
|
+
--button-hover-text-color: var(
|
|
256
|
+
--media-player-control-hover-color,
|
|
257
|
+
var(--media-player-control-color, #ffffff)
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.center-control {
|
|
262
|
+
--button-width: var(--media-player-center-control-size, 64px);
|
|
263
|
+
--button-height: var(--media-player-center-control-size, 64px);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.bottom-control {
|
|
267
|
+
--button-width: var(--media-player-bottom-control-size, 24px);
|
|
268
|
+
--button-height: var(--media-player-bottom-control-size, 24px);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.control :global(svg),
|
|
272
|
+
.control :global(img) {
|
|
273
|
+
height: var(--media-player-control-icon-size, 100%);
|
|
274
|
+
width: var(--media-player-control-icon-size, 100%);
|
|
275
|
+
object-fit: contain;
|
|
276
|
+
}
|
|
277
|
+
</style>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type MediaType = 'image' | 'video';
|
|
3
|
+
export type MediaPlayerProperties = MandatoryMediaPlayerProperties & OptionalMediaPlayerProperties & MediaPlayerEventProperties;
|
|
4
|
+
export type MandatoryMediaPlayerProperties = {
|
|
5
|
+
src: string;
|
|
6
|
+
type: MediaType;
|
|
7
|
+
};
|
|
8
|
+
export type OptionalMediaPlayerProperties = {
|
|
9
|
+
alt?: string;
|
|
10
|
+
fallback?: string;
|
|
11
|
+
autoplay?: boolean;
|
|
12
|
+
loop?: boolean;
|
|
13
|
+
controls?: boolean;
|
|
14
|
+
playing?: boolean;
|
|
15
|
+
muted?: boolean;
|
|
16
|
+
playIcon?: Snippet;
|
|
17
|
+
pauseIcon?: Snippet;
|
|
18
|
+
muteIcon?: Snippet;
|
|
19
|
+
unmuteIcon?: Snippet;
|
|
20
|
+
/** URL of a WebVTT captions file. Omit entirely to render no captions track at all
|
|
21
|
+
* (rather than an empty, non-functional one). */
|
|
22
|
+
captionsSrc?: string;
|
|
23
|
+
/** Label shown in the browser's caption/track menu. Only meaningful with captionsSrc. */
|
|
24
|
+
captionsLabel?: string;
|
|
25
|
+
/** BCP 47 language tag for the captions track, e.g. "en". Only meaningful with captionsSrc. */
|
|
26
|
+
captionsSrcLang?: string;
|
|
27
|
+
testId?: string;
|
|
28
|
+
classes?: string;
|
|
29
|
+
};
|
|
30
|
+
export type MediaPlayerEventProperties = {
|
|
31
|
+
onplay?: (event: Event) => void;
|
|
32
|
+
onpause?: (event: Event) => void;
|
|
33
|
+
onvolumechange?: (muted: boolean) => void;
|
|
34
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|