@streamscloud/kit 0.1.11 → 0.1.12-1772041668184
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/core/toastr/index.d.ts +1 -1
- package/dist/core/toastr/toastr.scss +38 -0
- package/dist/core/toastr/toastr.svelte.d.ts +1 -1
- package/dist/core/toastr/toastr.svelte.js +13 -6
- package/dist/core/toastr/types.d.ts +2 -0
- package/dist/ui/cropper/image-editor-dialog/cmp.image-editor-dialog.svelte +8 -7
- package/dist/ui/cropper/image-editor-dialog/index.d.ts +2 -2
- package/dist/ui/cropper/image-editor-dialog/index.js +1 -1
- package/dist/ui/cropper/image-editor-dialog/types.d.ts +11 -3
- package/dist/ui/cropper/img-cropper/cmp.img-cropper.svelte +1 -2
- package/dist/ui/cropper/img-cropper/img-cropper-base-worker.svelte.d.ts +8 -11
- package/dist/ui/cropper/img-cropper/img-cropper-base-worker.svelte.js +19 -7
- package/dist/ui/cropper/img-cropper/img-cropper-utils.d.ts +1 -0
- package/dist/ui/cropper/img-cropper/img-cropper-utils.js +23 -0
- package/dist/ui/cropper/img-cropper/img-cropper-worker.svelte.d.ts +9 -10
- package/dist/ui/cropper/img-cropper/img-cropper.svelte.d.ts +7 -19
- package/dist/ui/cropper/img-cropper/img-cropper.svelte.js +10 -5
- package/dist/ui/cropper/img-cropper/index.d.ts +1 -1
- package/dist/ui/dialog/cmp.dialog-container.svelte +12 -12
- package/dist/ui/dialog/dialog-data.d.ts +2 -0
- package/dist/ui/dialog/dialog-mount.d.ts +1 -1
- package/dist/ui/dialog/dialog-mount.js +2 -2
- package/dist/ui/dialog/dialogs.svelte.d.ts +3 -0
- package/dist/ui/dialog/dialogs.svelte.js +21 -2
- package/dist/ui/dialog/index.d.ts +1 -1
- package/dist/ui/dialog/index.js +1 -1
- package/dist/ui/dialog/types.svelte.d.ts +3 -14
- package/dist/ui/dialog/types.svelte.js +3 -18
- package/dist/ui/form-group/cmp.form-group-label.svelte +25 -0
- package/dist/ui/form-group/cmp.form-group-label.svelte.d.ts +8 -0
- package/dist/ui/form-group/cmp.form-group-note.svelte +16 -0
- package/dist/ui/form-group/cmp.form-group-note.svelte.d.ts +7 -0
- package/dist/ui/form-group/cmp.form-group.svelte +16 -0
- package/dist/ui/form-group/cmp.form-group.svelte.d.ts +8 -0
- package/dist/ui/form-group/index.d.ts +3 -0
- package/dist/ui/form-group/index.js +3 -0
- package/dist/ui/html-block/cmp.html-block.svelte +112 -0
- package/dist/ui/html-block/cmp.html-block.svelte.d.ts +7 -0
- package/dist/ui/html-block/index.d.ts +1 -0
- package/dist/ui/html-block/index.js +1 -0
- package/dist/ui/media-viewer-dialog/cmp.media-viewer-dialog.svelte +50 -0
- package/dist/ui/media-viewer-dialog/cmp.media-viewer-dialog.svelte.d.ts +9 -0
- package/dist/ui/media-viewer-dialog/index.d.ts +14 -0
- package/dist/ui/media-viewer-dialog/index.js +18 -0
- package/dist/ui/media-viewer-dialog/media-viewer-item.svelte +61 -0
- package/dist/ui/media-viewer-dialog/media-viewer-item.svelte.d.ts +7 -0
- package/dist/ui/media-viewer-dialog/types.d.ts +15 -0
- package/dist/ui/media-viewer-dialog/types.js +1 -0
- package/dist/ui/player/carousel/cmp.carousel.svelte +25 -4
- package/dist/ui/player/carousel/cmp.carousel.svelte.d.ts +3 -1
- package/dist/ui/player/feed-slider/cmp.feed-slider.svelte +3 -5
- package/dist/ui/player/utils/index.d.ts +1 -0
- package/dist/ui/player/utils/index.js +1 -0
- package/dist/ui/player/{feed-slider → utils}/wheel-gestures-adapter.d.ts +6 -2
- package/dist/ui/player/{feed-slider → utils}/wheel-gestures-adapter.js +22 -13
- package/dist/ui/video/cmp.video.svelte +16 -7
- package/package.json +14 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { Dialogs } from './dialogs.svelte';
|
|
2
2
|
export { DialogController } from './dialog-controller';
|
|
3
|
-
export {
|
|
3
|
+
export { DIALOG_SIZES, type DialogPosition, type DialogResult, type DialogSize } from './types.svelte';
|
|
4
4
|
export { default as Dialog } from './cmp.dialog.svelte';
|
|
5
5
|
export { default as DialogButton } from './cmp.dialog-button.svelte';
|
|
6
6
|
export { default as DialogCancelButton } from './cmp.dialog-cancel-button.svelte';
|
package/dist/ui/dialog/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { Dialogs } from './dialogs.svelte';
|
|
2
2
|
export { DialogController } from './dialog-controller';
|
|
3
|
-
export {
|
|
3
|
+
export { DIALOG_SIZES } from './types.svelte';
|
|
4
4
|
export { default as Dialog } from './cmp.dialog.svelte';
|
|
5
5
|
export { default as DialogButton } from './cmp.dialog-button.svelte';
|
|
6
6
|
export { default as DialogCancelButton } from './cmp.dialog-cancel-button.svelte';
|
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Default = "default",
|
|
5
|
-
Large = "large",
|
|
6
|
-
FullHD = "fullhd",
|
|
7
|
-
Auto = "auto"
|
|
8
|
-
}
|
|
9
|
-
export declare enum DialogPosition {
|
|
10
|
-
Center = "center",
|
|
11
|
-
CenterTop = "center-top",
|
|
12
|
-
FullScreen = "full-screen",
|
|
13
|
-
FullHeight = "full-height"
|
|
14
|
-
}
|
|
1
|
+
export type DialogSize = 'small' | 'medium' | 'default' | 'large' | 'fullhd' | 'auto';
|
|
2
|
+
export type DialogPosition = 'center' | 'center-top' | 'full-screen' | 'full-height';
|
|
3
|
+
export declare const DIALOG_SIZES: readonly DialogSize[];
|
|
15
4
|
export declare class DialogSettings {
|
|
16
5
|
closeOnClickOutside: boolean;
|
|
17
6
|
closeOnEsc: boolean;
|
|
@@ -1,25 +1,10 @@
|
|
|
1
|
-
export
|
|
2
|
-
(function (DialogSize) {
|
|
3
|
-
DialogSize["Small"] = "small";
|
|
4
|
-
DialogSize["Medium"] = "medium";
|
|
5
|
-
DialogSize["Default"] = "default";
|
|
6
|
-
DialogSize["Large"] = "large";
|
|
7
|
-
DialogSize["FullHD"] = "fullhd";
|
|
8
|
-
DialogSize["Auto"] = "auto";
|
|
9
|
-
})(DialogSize || (DialogSize = {}));
|
|
10
|
-
export var DialogPosition;
|
|
11
|
-
(function (DialogPosition) {
|
|
12
|
-
DialogPosition["Center"] = "center";
|
|
13
|
-
DialogPosition["CenterTop"] = "center-top";
|
|
14
|
-
DialogPosition["FullScreen"] = "full-screen";
|
|
15
|
-
DialogPosition["FullHeight"] = "full-height";
|
|
16
|
-
})(DialogPosition || (DialogPosition = {}));
|
|
1
|
+
export const DIALOG_SIZES = ['small', 'medium', 'default', 'large', 'fullhd', 'auto'];
|
|
17
2
|
export class DialogSettings {
|
|
18
3
|
closeOnClickOutside = $state(false);
|
|
19
4
|
closeOnEsc = $state(false);
|
|
20
5
|
nonCancelable = $state(false);
|
|
21
6
|
}
|
|
22
7
|
export class DialogContainerSettings {
|
|
23
|
-
size = $state(
|
|
24
|
-
position = $state(
|
|
8
|
+
size = $state('default');
|
|
9
|
+
position = $state('center-top');
|
|
25
10
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">let { withTooltip = false, children } = $props();
|
|
2
|
+
export {};
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<div class="form-group-label" class:form-group-label--with-tooltip={withTooltip}>
|
|
6
|
+
{@render children()}
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<style>.form-group-label {
|
|
10
|
+
--_form-group--label--font-size: var(--sc-kit--form-group--label--font-size, 0.875rem);
|
|
11
|
+
--_form-group--label--font-weight: var(--sc-kit--form-group--label--font-weight, 400);
|
|
12
|
+
--_form-group--label--color: var(--sc-kit--form-group--label--color);
|
|
13
|
+
--_form-group--label--margin-bottom: var(--sc-kit--form-group--label--margin-bottom, 0.5rem);
|
|
14
|
+
font-size: var(--_form-group--label--font-size);
|
|
15
|
+
padding: 0;
|
|
16
|
+
margin-bottom: var(--_form-group--label--margin-bottom);
|
|
17
|
+
font-weight: var(--_form-group--label--font-weight);
|
|
18
|
+
display: block;
|
|
19
|
+
color: var(--_form-group--label--color);
|
|
20
|
+
}
|
|
21
|
+
.form-group-label--with-tooltip {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
gap: 0.25rem;
|
|
25
|
+
}</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script lang="ts">let { children } = $props();
|
|
2
|
+
export {};
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<div class="form-group-note">
|
|
6
|
+
{@render children()}
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<style>.form-group-note {
|
|
10
|
+
--_form-group--note--font-size: var(--sc-kit--form-group--note--font-size, 0.875rem);
|
|
11
|
+
--_form-group--note--font-weight: var(--sc-kit--form-group--note--font-weight, 300);
|
|
12
|
+
margin-top: 0.5rem;
|
|
13
|
+
color: #9ca3af;
|
|
14
|
+
font-size: var(--_form-group--note--font-size);
|
|
15
|
+
font-weight: var(--_form-group--note--font-weight);
|
|
16
|
+
}</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script lang="ts">let { noMargin = false, children } = $props();
|
|
2
|
+
export {};
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<div class="form-group" class:form-group--no-margin={noMargin}>
|
|
6
|
+
{@render children()}
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<style>.form-group {
|
|
10
|
+
--_form-group--margin-bottom: var(--sc-kit--form-group--margin-bottom, 1.25rem);
|
|
11
|
+
display: block;
|
|
12
|
+
margin-bottom: var(--_form-group--margin-bottom);
|
|
13
|
+
}
|
|
14
|
+
.form-group--no-margin {
|
|
15
|
+
margin-bottom: 0;
|
|
16
|
+
}</style>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<script lang="ts">let { children } = $props();
|
|
2
|
+
export {};
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<div class="html-block">
|
|
6
|
+
{@render children()}
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<style>.html-block {
|
|
10
|
+
display: contents;
|
|
11
|
+
word-break: break-word;
|
|
12
|
+
font-size: 0.875em;
|
|
13
|
+
line-height: 1.4;
|
|
14
|
+
font-weight: normal;
|
|
15
|
+
color: light-dark(#2e2e2e, #ffffff);
|
|
16
|
+
}
|
|
17
|
+
.html-block :global(h1) {
|
|
18
|
+
font-size: 1.5em;
|
|
19
|
+
line-height: 1.4;
|
|
20
|
+
font-weight: 600;
|
|
21
|
+
color: light-dark(#2e2e2e, #ffffff);
|
|
22
|
+
}
|
|
23
|
+
.html-block :global(h2) {
|
|
24
|
+
font-size: 1.375em;
|
|
25
|
+
line-height: 1.4;
|
|
26
|
+
font-weight: 600;
|
|
27
|
+
color: light-dark(#2e2e2e, #ffffff);
|
|
28
|
+
}
|
|
29
|
+
.html-block :global(h3) {
|
|
30
|
+
font-size: 1.25em;
|
|
31
|
+
line-height: 1.4;
|
|
32
|
+
font-weight: 600;
|
|
33
|
+
color: light-dark(#2e2e2e, #ffffff);
|
|
34
|
+
}
|
|
35
|
+
.html-block :global(h4) {
|
|
36
|
+
font-size: 1.125em;
|
|
37
|
+
line-height: 1.4;
|
|
38
|
+
font-weight: 500;
|
|
39
|
+
color: light-dark(#2e2e2e, #ffffff);
|
|
40
|
+
}
|
|
41
|
+
.html-block :global(h5) {
|
|
42
|
+
font-size: 1.0625em;
|
|
43
|
+
line-height: 1.4;
|
|
44
|
+
font-weight: 500;
|
|
45
|
+
color: light-dark(#2e2e2e, #ffffff);
|
|
46
|
+
}
|
|
47
|
+
.html-block :global(h6) {
|
|
48
|
+
font-size: 1em;
|
|
49
|
+
line-height: 1.4;
|
|
50
|
+
font-weight: 500;
|
|
51
|
+
color: light-dark(#2e2e2e, #ffffff);
|
|
52
|
+
}
|
|
53
|
+
.html-block :global(a) {
|
|
54
|
+
font-size: 1em;
|
|
55
|
+
line-height: 1.4;
|
|
56
|
+
font-weight: normal;
|
|
57
|
+
color: light-dark(#2e2e2e, #ffffff);
|
|
58
|
+
color: light-dark(#144ab0, #5a8dec);
|
|
59
|
+
}
|
|
60
|
+
.html-block :global(pre) {
|
|
61
|
+
font-family: monospace;
|
|
62
|
+
white-space: pre-wrap;
|
|
63
|
+
margin: 1em 0;
|
|
64
|
+
font-size: 1em;
|
|
65
|
+
line-height: 1;
|
|
66
|
+
font-weight: normal;
|
|
67
|
+
color: light-dark(#2e2e2e, #ffffff);
|
|
68
|
+
}
|
|
69
|
+
.html-block :global(ul) :global(> li),
|
|
70
|
+
.html-block :global(ol) :global(> li) {
|
|
71
|
+
list-style: inherit;
|
|
72
|
+
margin-left: 1em;
|
|
73
|
+
position: relative;
|
|
74
|
+
font-size: 1em;
|
|
75
|
+
line-height: 1.4;
|
|
76
|
+
font-weight: normal;
|
|
77
|
+
color: light-dark(#2e2e2e, #ffffff);
|
|
78
|
+
}
|
|
79
|
+
.html-block :global(ol) {
|
|
80
|
+
list-style-type: decimal;
|
|
81
|
+
}
|
|
82
|
+
.html-block :global(ol) :global(> li > ol) {
|
|
83
|
+
list-style-type: lower-alpha;
|
|
84
|
+
}
|
|
85
|
+
.html-block :global(table) {
|
|
86
|
+
max-width: 100%;
|
|
87
|
+
}
|
|
88
|
+
.html-block :global(table),
|
|
89
|
+
.html-block :global(tbody),
|
|
90
|
+
.html-block :global(thead),
|
|
91
|
+
.html-block :global(tr),
|
|
92
|
+
.html-block :global(th),
|
|
93
|
+
.html-block :global(td) {
|
|
94
|
+
border-width: initial;
|
|
95
|
+
border-style: inherit;
|
|
96
|
+
border-color: inherit;
|
|
97
|
+
}
|
|
98
|
+
.html-block :global(ul:not([style*='list-style-type'])),
|
|
99
|
+
.html-block :global(ul:not([style*='list-style-type']) > li > ul:not([style*='list-style-type'])) {
|
|
100
|
+
list-style-type: none;
|
|
101
|
+
}
|
|
102
|
+
.html-block :global(ul:not([style*='list-style-type']) > li:before),
|
|
103
|
+
.html-block :global(ul:not([style*='list-style-type']) > li > ul:not([style*='list-style-type']) > li:before) {
|
|
104
|
+
content: "-";
|
|
105
|
+
text-indent: -1em;
|
|
106
|
+
position: absolute;
|
|
107
|
+
font-weight: bold;
|
|
108
|
+
}
|
|
109
|
+
.html-block :global(ul[style*='list-style-type'] > li:before),
|
|
110
|
+
.html-block :global(ul > li > ul[style*='list-style-type'] > li:before) {
|
|
111
|
+
content: none;
|
|
112
|
+
}</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as HtmlBlock } from './cmp.html-block.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as HtmlBlock } from './cmp.html-block.svelte';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script lang="ts">import { DialogCloseButton } from '../dialog';
|
|
2
|
+
import { Carousel } from '../player/carousel';
|
|
3
|
+
import { default as MediaViewerItem } from './media-viewer-item.svelte';
|
|
4
|
+
import { untrack } from 'svelte';
|
|
5
|
+
const { controller, data } = $props();
|
|
6
|
+
$effect(() => untrack(() => {
|
|
7
|
+
controller.updateSettings({ closeOnEsc: true, closeOnClickOutside: true });
|
|
8
|
+
controller.updateContainerSettings({ position: 'full-screen' });
|
|
9
|
+
}));
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<div class="media-viewer-dialog" style:--media-viewer-dialog--background--opacity={data.backgroundOpacity}>
|
|
13
|
+
<div class="media-viewer-dialog__close">
|
|
14
|
+
<DialogCloseButton controller={controller} />
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<div class="media-viewer-dialog__content">
|
|
18
|
+
{#if data.items.length === 1}
|
|
19
|
+
<MediaViewerItem item={data.items[0]} />
|
|
20
|
+
{:else}
|
|
21
|
+
<Carousel items={data.items} initialIndex={data.index} mode={data.carouselMode ?? 'arrows-with-counts'} wheelNavigation on={{ indexChanged: () => {} }}>
|
|
22
|
+
{#snippet children(item)}
|
|
23
|
+
<MediaViewerItem item={item} />
|
|
24
|
+
{/snippet}
|
|
25
|
+
</Carousel>
|
|
26
|
+
{/if}
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<style>.media-viewer-dialog {
|
|
31
|
+
--_media-viewer-dialog--background--opacity: var(--media-viewer-dialog--background--opacity, 0.6);
|
|
32
|
+
--sc-kit--dialog-close-button--color: #ffffff;
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
width: 100%;
|
|
36
|
+
height: 100%;
|
|
37
|
+
background: rgba(0, 0, 0, var(--_media-viewer-dialog--background--opacity));
|
|
38
|
+
position: relative;
|
|
39
|
+
padding: 0.625rem;
|
|
40
|
+
}
|
|
41
|
+
.media-viewer-dialog__close {
|
|
42
|
+
position: absolute;
|
|
43
|
+
top: 0.75rem;
|
|
44
|
+
right: 0.75rem;
|
|
45
|
+
z-index: 10;
|
|
46
|
+
}
|
|
47
|
+
.media-viewer-dialog__content {
|
|
48
|
+
flex: 1;
|
|
49
|
+
min-height: 0;
|
|
50
|
+
}</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type DialogController } from '../dialog';
|
|
2
|
+
import type { MediaViewerData } from './types';
|
|
3
|
+
type Props = {
|
|
4
|
+
controller: DialogController;
|
|
5
|
+
data: MediaViewerData;
|
|
6
|
+
};
|
|
7
|
+
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type Cmp = ReturnType<typeof Cmp>;
|
|
9
|
+
export default Cmp;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type DialogResult } from '../dialog';
|
|
2
|
+
import type { MediaViewerData } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Opens a full-screen media viewer dialog for images and videos.
|
|
5
|
+
* Single item renders directly; multiple items display in a carousel with arrow navigation.
|
|
6
|
+
*
|
|
7
|
+
* ### Options
|
|
8
|
+
* - `items` — array of media items to display
|
|
9
|
+
* - `index` — zero-based index of the initially visible item
|
|
10
|
+
* - `backgroundOpacity` — backdrop opacity (default `0.6`)
|
|
11
|
+
* - `carouselMode` — carousel navigation mode (default `'arrows-with-counts'`)
|
|
12
|
+
*/
|
|
13
|
+
export declare const openMediaViewer: (data: MediaViewerData) => Promise<DialogResult<void>>;
|
|
14
|
+
export type { MediaViewerData, MediaViewerItem, MediaViewerItemType } from './types';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Dialogs } from '../dialog';
|
|
2
|
+
import { default as MediaViewerDialog } from './cmp.media-viewer-dialog.svelte';
|
|
3
|
+
/**
|
|
4
|
+
* Opens a full-screen media viewer dialog for images and videos.
|
|
5
|
+
* Single item renders directly; multiple items display in a carousel with arrow navigation.
|
|
6
|
+
*
|
|
7
|
+
* ### Options
|
|
8
|
+
* - `items` — array of media items to display
|
|
9
|
+
* - `index` — zero-based index of the initially visible item
|
|
10
|
+
* - `backgroundOpacity` — backdrop opacity (default `0.6`)
|
|
11
|
+
* - `carouselMode` — carousel navigation mode (default `'arrows-with-counts'`)
|
|
12
|
+
*/
|
|
13
|
+
export const openMediaViewer = (data) => {
|
|
14
|
+
return Dialogs.open({
|
|
15
|
+
view: MediaViewerDialog,
|
|
16
|
+
data
|
|
17
|
+
});
|
|
18
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script lang="ts">import { Image } from '../image';
|
|
2
|
+
import { Video } from '../video';
|
|
3
|
+
const { item } = $props();
|
|
4
|
+
let containerWidth = $state(0);
|
|
5
|
+
let containerHeight = $state(0);
|
|
6
|
+
const fittedSize = $derived.by(() => {
|
|
7
|
+
if (containerWidth === 0 || containerHeight === 0) {
|
|
8
|
+
return { width: 0, height: 0 };
|
|
9
|
+
}
|
|
10
|
+
const aspectRatio = item.width / item.height;
|
|
11
|
+
const containerRatio = containerWidth / containerHeight;
|
|
12
|
+
if (aspectRatio > containerRatio) {
|
|
13
|
+
const width = containerWidth;
|
|
14
|
+
const height = width / aspectRatio;
|
|
15
|
+
return { width, height };
|
|
16
|
+
}
|
|
17
|
+
const height = containerHeight;
|
|
18
|
+
const width = height * aspectRatio;
|
|
19
|
+
return { width, height };
|
|
20
|
+
});
|
|
21
|
+
const observeResize = (node) => {
|
|
22
|
+
const observer = new ResizeObserver((entries) => {
|
|
23
|
+
const entry = entries[0];
|
|
24
|
+
if (entry) {
|
|
25
|
+
containerWidth = entry.contentRect.width;
|
|
26
|
+
containerHeight = entry.contentRect.height;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
observer.observe(node);
|
|
30
|
+
return {
|
|
31
|
+
destroy: () => observer.disconnect()
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<div class="media-viewer-item" use:observeResize>
|
|
37
|
+
{#if fittedSize.width > 0 && fittedSize.height > 0}
|
|
38
|
+
<div class="media-viewer-item__media" style:width="{fittedSize.width}px" style:height="{fittedSize.height}px">
|
|
39
|
+
{#if item.type === 'image'}
|
|
40
|
+
<Image src={item.url} alt="" />
|
|
41
|
+
{:else}
|
|
42
|
+
<Video src={item.url} poster={item.thumbnailUrl} autoplay="on-appearance" />
|
|
43
|
+
{/if}
|
|
44
|
+
</div>
|
|
45
|
+
{/if}
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<style>.media-viewer-item {
|
|
49
|
+
--sc-kit--image--object-fit: contain;
|
|
50
|
+
--sc-kit--video--media-fit: contain;
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
justify-content: center;
|
|
54
|
+
width: 100%;
|
|
55
|
+
height: 100%;
|
|
56
|
+
}
|
|
57
|
+
.media-viewer-item__media {
|
|
58
|
+
flex-shrink: 0;
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
border-radius: 0.375rem;
|
|
61
|
+
}</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CarouselMode } from '../player/carousel';
|
|
2
|
+
export type MediaViewerItemType = 'image' | 'video';
|
|
3
|
+
export type MediaViewerItem = {
|
|
4
|
+
url: string;
|
|
5
|
+
thumbnailUrl: string | null;
|
|
6
|
+
type: MediaViewerItemType;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
};
|
|
10
|
+
export type MediaViewerData = {
|
|
11
|
+
items: MediaViewerItem[];
|
|
12
|
+
index: number;
|
|
13
|
+
backgroundOpacity?: number;
|
|
14
|
+
carouselMode?: CarouselMode;
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script lang="ts" generics="T">import { Utils, isBrowser } from '../../../core/utils';
|
|
2
2
|
import { Icon } from '../../icon';
|
|
3
|
-
import { TouchSynchronizer } from '../utils';
|
|
3
|
+
import { TouchSynchronizer, createWheelAdapter } from '../utils';
|
|
4
4
|
import { CarouselLocalization } from './carousel-localization';
|
|
5
5
|
import IconChevronLeft from '@fluentui/svg-icons/icons/chevron_left_20_regular.svg?raw';
|
|
6
6
|
import IconChevronRight from '@fluentui/svg-icons/icons/chevron_right_20_regular.svg?raw';
|
|
7
7
|
import { onDestroy, onMount, untrack } from 'svelte';
|
|
8
|
-
let { items, mode = 'arrows-with-counts', initialIndex, autoSlideMs = 0, on, dot, children } = $props();
|
|
8
|
+
let { items, mode = 'arrows-with-counts', initialIndex, autoSlideMs = 0, wheelNavigation = false, on, dot, children } = $props();
|
|
9
9
|
const localization = new CarouselLocalization();
|
|
10
10
|
const itemIndices = $derived(items.map((_, index) => index));
|
|
11
11
|
const animationDuration = 300;
|
|
@@ -17,6 +17,20 @@ let slidesRef;
|
|
|
17
17
|
let sliderWidth = $state(0);
|
|
18
18
|
let swipeTransition = $state(0);
|
|
19
19
|
let resizeObserver;
|
|
20
|
+
let wheelAdapter = null;
|
|
21
|
+
const wheelCallbacks = {
|
|
22
|
+
canLoadNext: () => items.length > 1,
|
|
23
|
+
canLoadPrevious: () => items.length > 1,
|
|
24
|
+
onTrigger: (direction) => {
|
|
25
|
+
if (direction === 'next') {
|
|
26
|
+
loadNext();
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
loadPrevious();
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
getAnimationDurationMs: () => animationDuration
|
|
33
|
+
};
|
|
20
34
|
onMount(() => {
|
|
21
35
|
notifyIndexChanged();
|
|
22
36
|
setTimeout(() => {
|
|
@@ -132,6 +146,12 @@ onMount(() => {
|
|
|
132
146
|
if (isBrowser()) {
|
|
133
147
|
window.addEventListener(`keydown`, onKeyPress);
|
|
134
148
|
}
|
|
149
|
+
if (wheelNavigation) {
|
|
150
|
+
wheelAdapter = createWheelAdapter(slidesRef, {
|
|
151
|
+
axis: 'x',
|
|
152
|
+
cbs: wheelCallbacks
|
|
153
|
+
});
|
|
154
|
+
}
|
|
135
155
|
});
|
|
136
156
|
onDestroy(() => {
|
|
137
157
|
if (resizeObserver) {
|
|
@@ -143,6 +163,7 @@ onDestroy(() => {
|
|
|
143
163
|
window.clearInterval(interval);
|
|
144
164
|
}
|
|
145
165
|
}
|
|
166
|
+
wheelAdapter?.destroy();
|
|
146
167
|
});
|
|
147
168
|
const onKeyPress = Utils.throttle((e) => {
|
|
148
169
|
if (e.key === 'ArrowLeft') {
|
|
@@ -260,7 +281,7 @@ A horizontal slide carousel with infinite looping, touch swipe support, keyboard
|
|
|
260
281
|
### CSS Custom Properties
|
|
261
282
|
| Property | Description | Default |
|
|
262
283
|
|---|---|---|
|
|
263
|
-
| `--sc-kit--carousel--button-color` | Navigation arrow button background | `
|
|
284
|
+
| `--sc-kit--carousel--button-color` | Navigation arrow button background | `rgba(255, 255, 255, 0.2)` |
|
|
264
285
|
| `--sc-kit--carousel--dot-color` | Dot indicator color (fill when active, border when inactive) | `white` |
|
|
265
286
|
| `--sc-kit--carousel--dot-size` | Dot indicator diameter | `0.5rem` |
|
|
266
287
|
| `--sc-kit--carousel--text-color` | Arrow button icon and counter text color | `white` |
|
|
@@ -269,7 +290,7 @@ A horizontal slide carousel with infinite looping, touch swipe support, keyboard
|
|
|
269
290
|
<style>@charset "UTF-8";
|
|
270
291
|
.carousel {
|
|
271
292
|
/* Public API */
|
|
272
|
-
--_carousel--button-color: var(--sc-kit--carousel--button-color,
|
|
293
|
+
--_carousel--button-color: var(--sc-kit--carousel--button-color, rgba(255, 255, 255, 0.2));
|
|
273
294
|
--_carousel--dot-color: var(--sc-kit--carousel--dot-color, #ffffff);
|
|
274
295
|
--_carousel--dot-size: var(--sc-kit--carousel--dot-size, 0.5rem);
|
|
275
296
|
--_carousel--text-color: var(--sc-kit--carousel--text-color, #ffffff);
|
|
@@ -10,6 +10,8 @@ declare function $$render<T>(): {
|
|
|
10
10
|
mode?: CarouselMode;
|
|
11
11
|
/** Auto-advance interval in ms; 0 disables auto-sliding @default 0 */
|
|
12
12
|
autoSlideMs?: number;
|
|
13
|
+
/** Convert vertical mouse wheel scroll to slide navigation @default false */
|
|
14
|
+
wheelNavigation?: boolean;
|
|
13
15
|
on?: {
|
|
14
16
|
/** Fires after the active slide changes */
|
|
15
17
|
indexChanged: (index: number) => void;
|
|
@@ -46,7 +48,7 @@ interface $$IsomorphicComponent {
|
|
|
46
48
|
* ### CSS Custom Properties
|
|
47
49
|
* | Property | Description | Default |
|
|
48
50
|
* |---|---|---|
|
|
49
|
-
* | `--sc-kit--carousel--button-color` | Navigation arrow button background | `
|
|
51
|
+
* | `--sc-kit--carousel--button-color` | Navigation arrow button background | `rgba(255, 255, 255, 0.2)` |
|
|
50
52
|
* | `--sc-kit--carousel--dot-color` | Dot indicator color (fill when active, border when inactive) | `white` |
|
|
51
53
|
* | `--sc-kit--carousel--dot-size` | Dot indicator diameter | `0.5rem` |
|
|
52
54
|
* | `--sc-kit--carousel--text-color` | Arrow button icon and counter text color | `white` |
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
<script lang="ts" generics="T extends { id: string }">import { TouchSynchronizer } from '../utils';
|
|
2
|
-
import { createWheelAdapter } from './wheel-gestures-adapter';
|
|
1
|
+
<script lang="ts" generics="T extends { id: string }">import { TouchSynchronizer, createWheelAdapter } from '../utils';
|
|
3
2
|
import { onDestroy, onMount, untrack } from 'svelte';
|
|
4
3
|
let { buffer, on, children } = $props();
|
|
5
4
|
let slidesRef;
|
|
@@ -142,11 +141,10 @@ const wheelCallbacks = {
|
|
|
142
141
|
canLoadNext: () => buffer.canLoadNext,
|
|
143
142
|
canLoadPrevious: () => buffer.canLoadPrevious,
|
|
144
143
|
onTrigger: (direction) => {
|
|
145
|
-
|
|
146
|
-
if (direction > 0) {
|
|
144
|
+
if (direction === 'next') {
|
|
147
145
|
buffer.loadNext();
|
|
148
146
|
}
|
|
149
|
-
else
|
|
147
|
+
else {
|
|
150
148
|
buffer.loadPrevious();
|
|
151
149
|
}
|
|
152
150
|
},
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
+
export type WheelAdapterAxis = 'x' | 'y';
|
|
2
|
+
export type WheelAdapterDirection = 'next' | 'previous';
|
|
1
3
|
export type WheelAdapterCallbacks = {
|
|
2
4
|
canLoadNext: () => boolean;
|
|
3
5
|
canLoadPrevious: () => boolean;
|
|
4
|
-
onTrigger: (direction:
|
|
6
|
+
onTrigger: (direction: WheelAdapterDirection) => void;
|
|
5
7
|
getAnimationDurationMs: () => number;
|
|
6
8
|
};
|
|
7
9
|
/**
|
|
8
10
|
* Minimal, robust wheel adapter:
|
|
9
|
-
* -
|
|
11
|
+
* - Configurable axis: 'y' reads Y only; 'x' prefers X but remaps Y when X is negligible
|
|
12
|
+
* - EMA over velocity to smooth noisy streams (esp. on Windows)
|
|
10
13
|
* - Cooldown blocks triggers while animation runs
|
|
11
14
|
* - Mouse fallback: if velocity is near zero but delta is large, treat as a discrete "kick"
|
|
12
15
|
*/
|
|
13
16
|
export declare const createWheelAdapter: (target: HTMLElement, params: {
|
|
17
|
+
axis?: WheelAdapterAxis;
|
|
14
18
|
cbs: WheelAdapterCallbacks;
|
|
15
19
|
}) => {
|
|
16
20
|
destroy(): void;
|