@rixl/media-vue 0.0.0-stage → 1.0.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 CHANGED
@@ -1,3 +1,36 @@
1
- # Temporary Holding Version
1
+ # Rixl Media SDK for Vue
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Vue 3 media components backed by the shared Lit UI.
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install @rixl/media-vue
9
+ ```
10
+
11
+ ## Setup & Usage
12
+
13
+ ```vue
14
+ <script setup lang="ts">
15
+ import {onMounted} from "vue";
16
+ import {connect} from "@rixl/media-vue/core";
17
+ import {Image, Video, Feed} from "@rixl/media-vue";
18
+
19
+ onMounted(async () => {
20
+ await connect({baseUrl: "https://api.rixl.com", apiKey: "your-api-key"});
21
+ });
22
+ </script>
23
+
24
+ <template>
25
+ <Image id="your-image-id" alt="Landscape" />
26
+ <Video id="your-video-id" :muted="true" />
27
+ <Feed feedId="your-feed-id" />
28
+ </template>
29
+ ```
30
+
31
+ ### Subpath Exports
32
+
33
+ - `@rixl/media-vue/core` — Headless state stores, API clients, and connection helpers
34
+ - `@rixl/media-vue/image` — `<Image>` component
35
+ - `@rixl/media-vue/video` — `<Video>` and `<VideoPlayer>` components
36
+ - `@rixl/media-vue/feed` — `<Feed>` component
@@ -0,0 +1,22 @@
1
+ import { FeedPost, FontFamilyKey, Page, PlayerLanguage } from "@rixl/media-core";
2
+ import "@rixl/media-lit/feed";
3
+ import { DefineSetupFnComponent } from "vue";
4
+ export type FeedProps = {
5
+ feedId?: string;
6
+ lang?: PlayerLanguage;
7
+ feedFont?: FontFamilyKey;
8
+ loop?: boolean;
9
+ autoscroll?: boolean;
10
+ muted?: boolean;
11
+ analytics?: boolean;
12
+ analyticsPage?: Page;
13
+ safeAreaTabBar?: number;
14
+ posts?: FeedPost[];
15
+ initialIndex?: number;
16
+ onFetchMore?: () => void | Promise<void>;
17
+ };
18
+ export type FeedEmits = {
19
+ muteChange: (muted: boolean) => boolean;
20
+ "update:muted": (muted: boolean) => boolean;
21
+ };
22
+ export declare const Feed: DefineSetupFnComponent<FeedProps, FeedEmits>;
@@ -0,0 +1,93 @@
1
+ import { omitUndefined } from "../../lib/element-props.js";
2
+ import "@rixl/media-lit/feed";
3
+ import { defineComponent, h, ref } from "vue";
4
+
5
+ // src/components/feed/feed.ts
6
+ const Feed = defineComponent({
7
+ name: "RixlFeed",
8
+ props: {
9
+ feedId: {
10
+ type: String,
11
+ default: ""
12
+ },
13
+ lang: {
14
+ type: String,
15
+ default: "en"
16
+ },
17
+ feedFont: {
18
+ type: String,
19
+ default: void 0
20
+ },
21
+ loop: {
22
+ type: Boolean,
23
+ default: false
24
+ },
25
+ autoscroll: {
26
+ type: Boolean,
27
+ default: false
28
+ },
29
+ muted: {
30
+ type: Boolean,
31
+ default: void 0
32
+ },
33
+ analytics: {
34
+ type: Boolean,
35
+ default: void 0
36
+ },
37
+ analyticsPage: {
38
+ type: String,
39
+ default: void 0
40
+ },
41
+ safeAreaTabBar: {
42
+ type: Number,
43
+ default: void 0
44
+ },
45
+ posts: {
46
+ type: Array,
47
+ default: void 0
48
+ },
49
+ initialIndex: {
50
+ type: Number,
51
+ default: void 0
52
+ },
53
+ onFetchMore: {
54
+ type: Function,
55
+ default: void 0
56
+ }
57
+ },
58
+ emits: {
59
+ muteChange: (_muted) => true,
60
+ "update:muted": (_muted) => true
61
+ },
62
+ setup(props, { attrs, emit, expose }) {
63
+ const element = ref(null);
64
+ expose({ element });
65
+ function handleMuteChange(event) {
66
+ if (event instanceof CustomEvent && typeof event.detail?.muted === "boolean") {
67
+ emit("muteChange", event.detail.muted);
68
+ emit("update:muted", event.detail.muted);
69
+ }
70
+ }
71
+ return () => h("rixl-feed", {
72
+ ...attrs,
73
+ ref: element,
74
+ ...omitUndefined({
75
+ feedId: props.feedId,
76
+ lang: props.lang,
77
+ feedFont: props.feedFont,
78
+ loop: props.loop,
79
+ autoscroll: props.autoscroll,
80
+ muted: props.muted,
81
+ analytics: props.analytics,
82
+ analyticsPage: props.analyticsPage,
83
+ safeAreaTabBar: props.safeAreaTabBar,
84
+ posts: props.posts,
85
+ initialIndex: props.initialIndex,
86
+ onFetchMore: props.onFetchMore
87
+ }),
88
+ "onMute-change": handleMuteChange
89
+ });
90
+ }
91
+ });
92
+
93
+ export { Feed };
@@ -0,0 +1,17 @@
1
+ import { ImageData, Page } from "@rixl/media-core";
2
+ import { DefineSetupFnComponent } from "vue";
3
+ import "@rixl/media-lit/image";
4
+ export type ImageProps = {
5
+ id?: string;
6
+ imageData?: ImageData;
7
+ alt?: string;
8
+ analytics?: boolean;
9
+ analyticsPage?: Page;
10
+ isCurrent?: boolean;
11
+ feedId?: string;
12
+ postId?: string;
13
+ };
14
+ export type ImageEmits = {
15
+ loadingChange: (isLoading: boolean) => boolean;
16
+ };
17
+ export declare const Image: DefineSetupFnComponent<ImageProps, ImageEmits>;
@@ -0,0 +1,71 @@
1
+ import { omitUndefined } from "../../lib/element-props.js";
2
+ import { defineComponent, h, ref } from "vue";
3
+ import "@rixl/media-lit/image";
4
+
5
+ // src/components/image/image.ts
6
+ function resolveIsCurrent(isCurrent, id) {
7
+ if (isCurrent !== void 0) return isCurrent;
8
+ return id ? true : void 0;
9
+ }
10
+ const Image = defineComponent({
11
+ name: "RixlImage",
12
+ props: {
13
+ id: {
14
+ type: String,
15
+ default: void 0
16
+ },
17
+ imageData: {
18
+ type: Object,
19
+ default: void 0
20
+ },
21
+ alt: {
22
+ type: String,
23
+ default: void 0
24
+ },
25
+ analytics: {
26
+ type: Boolean,
27
+ default: true
28
+ },
29
+ analyticsPage: {
30
+ type: String,
31
+ default: "standalone"
32
+ },
33
+ isCurrent: {
34
+ type: Boolean,
35
+ default: void 0
36
+ },
37
+ feedId: {
38
+ type: String,
39
+ default: void 0
40
+ },
41
+ postId: {
42
+ type: String,
43
+ default: void 0
44
+ }
45
+ },
46
+ emits: { loadingChange: (_isLoading) => true },
47
+ setup(props, { attrs, emit, expose }) {
48
+ const element = ref(null);
49
+ expose({ element });
50
+ function handleLoadingChange(event) {
51
+ if (event instanceof CustomEvent && typeof event.detail === "boolean") emit("loadingChange", event.detail);
52
+ }
53
+ return () => h("rixl-image", {
54
+ ...attrs,
55
+ ref: element,
56
+ ...omitUndefined({
57
+ imageId: props.id,
58
+ imageData: props.imageData,
59
+ alt: props.alt,
60
+ analytics: props.analytics,
61
+ analyticsPage: props.analyticsPage,
62
+ isCurrent: resolveIsCurrent(props.isCurrent, props.id),
63
+ feedId: props.feedId,
64
+ postId: props.postId
65
+ }),
66
+ "onLoading-change": handleLoadingChange
67
+ });
68
+ }
69
+ });
70
+
71
+ export { Image };
@@ -0,0 +1,5 @@
1
+ import { DefineSetupFnComponent, SlotsType, VNode } from "vue";
2
+ export type VideoPlayerSlots = SlotsType<{
3
+ default?: () => VNode[];
4
+ }>;
5
+ export declare const VideoPlayer: DefineSetupFnComponent<Record<string, never>, Record<string, never>, VideoPlayerSlots>;
@@ -0,0 +1,14 @@
1
+ import { providePlayerScope } from "../../lib/player-scope.js";
2
+ import { defineComponent } from "vue";
3
+
4
+ // src/components/video/video-player.ts
5
+ const VideoPlayer = defineComponent({
6
+ name: "RixlVideoPlayer",
7
+ setup(_props, { slots }) {
8
+ const playerId = crypto.randomUUID();
9
+ providePlayerScope(playerId);
10
+ return () => slots.default?.() ?? null;
11
+ }
12
+ });
13
+
14
+ export { VideoPlayer };
@@ -0,0 +1,44 @@
1
+ import { FeedPost, FontFamilyKey, Page, VideoData, VideoHeatmap, VideoTheme } from "@rixl/media-core";
2
+ import "vue";
3
+ export type VideoLoadMode = "unloaded" | "idle" | "preloading" | "playing";
4
+ export type VideoPlanType = "free" | "pro" | "pay-as-you-go" | "custom";
5
+ export type VideoProps = {
6
+ id?: string;
7
+ src?: string;
8
+ videoData?: VideoData;
9
+ autoPlay?: boolean;
10
+ muted?: boolean;
11
+ loop?: boolean;
12
+ playsInline?: boolean;
13
+ poster?: string;
14
+ thumbhash?: string;
15
+ volume?: number;
16
+ progressBar?: boolean;
17
+ heatmap?: boolean;
18
+ chapters?: boolean;
19
+ heatmapData?: VideoHeatmap | null;
20
+ controls?: boolean;
21
+ hideUI?: boolean;
22
+ settingsOpen?: boolean;
23
+ theme?: VideoTheme;
24
+ autoHideMs?: number;
25
+ allowPlayPause?: boolean;
26
+ allowFullscreen?: boolean;
27
+ allowPictureInPicture?: boolean;
28
+ soundDisabled?: boolean;
29
+ resumeProgress?: boolean;
30
+ isCurrent?: boolean;
31
+ isFeed?: boolean;
32
+ loadState?: VideoLoadMode;
33
+ dataPost?: FeedPost;
34
+ feedFont?: FontFamilyKey;
35
+ feedId?: string;
36
+ postId?: string;
37
+ analytics?: boolean;
38
+ analyticsPage?: Page;
39
+ lang?: string;
40
+ planType?: VideoPlanType;
41
+ userId?: string;
42
+ userProperties?: Record<string, string>;
43
+ hotSegments?: boolean;
44
+ };
@@ -0,0 +1,82 @@
1
+ // src/components/video/video-props.ts
2
+ const optionalBoolean = {
3
+ type: Boolean,
4
+ default: void 0
5
+ };
6
+ const optionalString = {
7
+ type: String,
8
+ default: void 0
9
+ };
10
+ const optionalNumber = {
11
+ type: Number,
12
+ default: void 0
13
+ };
14
+ const videoPropDefinitions = {
15
+ id: optionalString,
16
+ src: optionalString,
17
+ videoData: {
18
+ type: Object,
19
+ default: void 0
20
+ },
21
+ autoPlay: optionalBoolean,
22
+ muted: optionalBoolean,
23
+ loop: optionalBoolean,
24
+ playsInline: optionalBoolean,
25
+ poster: optionalString,
26
+ thumbhash: optionalString,
27
+ volume: optionalNumber,
28
+ progressBar: optionalBoolean,
29
+ heatmap: optionalBoolean,
30
+ chapters: optionalBoolean,
31
+ heatmapData: {
32
+ type: Object,
33
+ default: void 0
34
+ },
35
+ controls: optionalBoolean,
36
+ hideUI: optionalBoolean,
37
+ settingsOpen: optionalBoolean,
38
+ theme: {
39
+ type: String,
40
+ default: void 0
41
+ },
42
+ autoHideMs: optionalNumber,
43
+ allowPlayPause: optionalBoolean,
44
+ allowFullscreen: optionalBoolean,
45
+ allowPictureInPicture: optionalBoolean,
46
+ soundDisabled: optionalBoolean,
47
+ resumeProgress: optionalBoolean,
48
+ isCurrent: optionalBoolean,
49
+ isFeed: optionalBoolean,
50
+ loadState: {
51
+ type: String,
52
+ default: void 0
53
+ },
54
+ dataPost: {
55
+ type: Object,
56
+ default: void 0
57
+ },
58
+ feedFont: {
59
+ type: String,
60
+ default: void 0
61
+ },
62
+ feedId: optionalString,
63
+ postId: optionalString,
64
+ analytics: optionalBoolean,
65
+ analyticsPage: {
66
+ type: String,
67
+ default: void 0
68
+ },
69
+ lang: optionalString,
70
+ planType: {
71
+ type: String,
72
+ default: void 0
73
+ },
74
+ userId: optionalString,
75
+ userProperties: {
76
+ type: Object,
77
+ default: void 0
78
+ },
79
+ hotSegments: optionalBoolean
80
+ };
81
+
82
+ export { videoPropDefinitions };
@@ -0,0 +1,21 @@
1
+ import { VideoProps } from "./video-props.js";
2
+ import { DefineSetupFnComponent, SlotsType, VNode } from "vue";
3
+ import "@rixl/media-lit/video";
4
+ export type VideoEmits = {
5
+ play: (event: Event) => boolean;
6
+ pause: (event: Event) => boolean;
7
+ pausedChange: (isPaused: boolean) => boolean;
8
+ ended: () => boolean;
9
+ timeUpdate: (event: Event) => boolean;
10
+ loadedMetadata: (event: Event) => boolean;
11
+ error: (event: Event) => boolean;
12
+ volumeChange: (event: Event) => boolean;
13
+ loadingChange: (isLoading: boolean) => boolean;
14
+ rateChange: (event: Event) => boolean;
15
+ qualityChange: (event: Event) => boolean;
16
+ };
17
+ export type VideoSlots = SlotsType<{
18
+ default?: () => VNode[];
19
+ }>;
20
+ export declare const Video: DefineSetupFnComponent<VideoProps, VideoEmits, VideoSlots>;
21
+ export type { VideoProps };
@@ -0,0 +1,70 @@
1
+ import { omitUndefined } from "../../lib/element-props.js";
2
+ import { providePlayerScope, useOptionalPlayerId } from "../../lib/player-scope.js";
3
+ import { videoPropDefinitions } from "./video-props.js";
4
+ import { defineComponent, h, ref } from "vue";
5
+ import "@rixl/media-lit/video";
6
+
7
+ // src/components/video/video.ts
8
+ const videoEmits = {
9
+ play: (_event) => true,
10
+ pause: (_event) => true,
11
+ pausedChange: (_isPaused) => true,
12
+ ended: () => true,
13
+ timeUpdate: (_event) => true,
14
+ loadedMetadata: (_event) => true,
15
+ error: (_event) => true,
16
+ volumeChange: (_event) => true,
17
+ loadingChange: (_isLoading) => true,
18
+ rateChange: (_event) => true,
19
+ qualityChange: (_event) => true
20
+ };
21
+ function buildVideoEventListeners(emit) {
22
+ return {
23
+ onPlay: (event) => emit("play", event),
24
+ onPause: (event) => emit("pause", event),
25
+ "onPaused-change": (event) => {
26
+ const detail = event.detail;
27
+ emit("pausedChange", detail.paused);
28
+ },
29
+ onEnded: () => emit("ended"),
30
+ onTimeupdate: (event) => emit("timeUpdate", event),
31
+ "onLoaded-metadata": (event) => emit("loadedMetadata", event),
32
+ onError: (event) => emit("error", event),
33
+ "onVolume-change": (event) => emit("volumeChange", event),
34
+ "onLoading-change": (event) => {
35
+ const detail = event.detail;
36
+ emit("loadingChange", detail.loading);
37
+ },
38
+ "onRate-change": (event) => emit("rateChange", event),
39
+ "onQuality-change": (event) => emit("qualityChange", event)
40
+ };
41
+ }
42
+ function mapVideoElementProps(props, playerId) {
43
+ const { id, chapters, ...rest } = props;
44
+ return omitUndefined({
45
+ ...rest,
46
+ videoId: id,
47
+ playerId,
48
+ showChapters: chapters
49
+ });
50
+ }
51
+ const Video = defineComponent({
52
+ name: "RixlVideo",
53
+ props: videoPropDefinitions,
54
+ emits: videoEmits,
55
+ setup(props, { attrs, emit, expose, slots }) {
56
+ const playerId = useOptionalPlayerId() ?? crypto.randomUUID();
57
+ providePlayerScope(playerId);
58
+ const element = ref(null);
59
+ expose({ element });
60
+ const listeners = buildVideoEventListeners(emit);
61
+ return () => h("rixl-video", {
62
+ ...attrs,
63
+ ref: element,
64
+ ...mapVideoElementProps(props, playerId),
65
+ ...listeners
66
+ }, slots.default?.());
67
+ }
68
+ });
69
+
70
+ export { Video, videoEmits };
package/dist/core.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "@rixl/media-core";
package/dist/core.js ADDED
@@ -0,0 +1 @@
1
+ export * from "@rixl/media-core"
package/dist/feed.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { Feed, FeedProps } from "./components/feed/feed.js";
2
+ export { Feed, type FeedProps };
package/dist/feed.js ADDED
@@ -0,0 +1,3 @@
1
+ import { Feed } from "./components/feed/feed.js";
2
+
3
+ export { Feed };
@@ -0,0 +1,2 @@
1
+ import { Image, ImageProps } from "./components/image/image.js";
2
+ export { Image, type ImageProps };
package/dist/image.js ADDED
@@ -0,0 +1,3 @@
1
+ import { Image } from "./components/image/image.js";
2
+
3
+ export { Image };
@@ -0,0 +1,11 @@
1
+ import { Feed, FeedProps } from "./components/feed/feed.js";
2
+ import "./feed.js";
3
+ import { Image, ImageProps } from "./components/image/image.js";
4
+ import "./image.js";
5
+ import { VideoProps } from "./components/video/video-props.js";
6
+ import { Video } from "./components/video/video.js";
7
+ import { VideoPlayer } from "./components/video/video-player.js";
8
+ import { useOptionalPlayerId, usePlayerId } from "./lib/player-scope.js";
9
+ import "./video.js";
10
+ export * from "@rixl/media-core";
11
+ export { Feed, type FeedProps, Image, type ImageProps, Video, VideoPlayer, type VideoProps, useOptionalPlayerId, usePlayerId };
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ import { Feed } from "./components/feed/feed.js";
2
+ import { Image } from "./components/image/image.js";
3
+ import { Video } from "./components/video/video.js";
4
+ import { VideoPlayer } from "./components/video/video-player.js";
5
+ import { useOptionalPlayerId, usePlayerId } from "./lib/player-scope.js";
6
+
7
+ export * from "@rixl/media-core"
8
+
9
+ export { Feed, Image, Video, VideoPlayer, useOptionalPlayerId, usePlayerId };
@@ -0,0 +1,10 @@
1
+ import { toRaw } from "vue";
2
+
3
+ // src/lib/element-props.ts
4
+ function omitUndefined(record) {
5
+ const result = {};
6
+ for (const [key, value] of Object.entries(record)) if (value !== void 0) result[key] = typeof value === "object" && value !== null ? toRaw(value) : value;
7
+ return result;
8
+ }
9
+
10
+ export { omitUndefined };
@@ -0,0 +1,3 @@
1
+ import { UUID } from "@rixl/media-core";
2
+ export declare function useOptionalPlayerId(): UUID | null;
3
+ export declare function usePlayerId(): UUID;
@@ -0,0 +1,25 @@
1
+ import { inject, provide } from "vue";
2
+
3
+ // src/lib/player-scope.ts
4
+ const PLAYER_SCOPE_KEY = Symbol("rixl-player-scope");
5
+ function getOutOfScopeErrorMessage(fnName) {
6
+ return `${fnName}() must be used inside <Video> or <VideoPlayer>.\n\nFix:
7
+
8
+ <VideoPlayer>
9
+ <Video id="..." theme="hideUI" />
10
+ <YourControls />
11
+ </VideoPlayer>`;
12
+ }
13
+ function providePlayerScope(playerId) {
14
+ provide(PLAYER_SCOPE_KEY, playerId);
15
+ }
16
+ function useOptionalPlayerId() {
17
+ return inject(PLAYER_SCOPE_KEY, null);
18
+ }
19
+ function usePlayerId() {
20
+ const playerId = useOptionalPlayerId();
21
+ if (!playerId) throw new Error(getOutOfScopeErrorMessage("usePlayerId"));
22
+ return playerId;
23
+ }
24
+
25
+ export { providePlayerScope, useOptionalPlayerId, usePlayerId };
@@ -0,0 +1,5 @@
1
+ import { VideoProps } from "./components/video/video-props.js";
2
+ import { Video } from "./components/video/video.js";
3
+ import { VideoPlayer } from "./components/video/video-player.js";
4
+ import { useOptionalPlayerId, usePlayerId } from "./lib/player-scope.js";
5
+ export { Video, VideoPlayer, type VideoProps, useOptionalPlayerId, usePlayerId };
package/dist/video.js ADDED
@@ -0,0 +1,5 @@
1
+ import { Video } from "./components/video/video.js";
2
+ import { VideoPlayer } from "./components/video/video-player.js";
3
+ import { useOptionalPlayerId, usePlayerId } from "./lib/player-scope.js";
4
+
5
+ export { Video, VideoPlayer, useOptionalPlayerId, usePlayerId };
package/package.json CHANGED
@@ -1,6 +1,54 @@
1
1
  {
2
2
  "name": "@rixl/media-vue",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.1",
4
+ "description": "Vue 3 media components for Rixl — adaptive HLS video player, image components with thumbhash, and infinite feeds.",
5
+ "keywords": [
6
+ "feed",
7
+ "hls",
8
+ "image",
9
+ "player",
10
+ "rixl",
11
+ "sdk",
12
+ "video",
13
+ "vue",
14
+ "vue3",
15
+ "web"
16
+ ],
17
+ "homepage": "https://rixl.com",
18
+ "license": "SEE LICENSE IN LICENSE.md",
19
+ "author": "Rixl Inc.",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/rixlhq/mediasdk.git",
23
+ "directory": "packages/vue"
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "type": "module",
29
+ "sideEffects": false,
30
+ "types": "dist/index.d.ts",
31
+ "exports": {
32
+ ".": "./dist/index.js",
33
+ "./core": "./dist/core.js",
34
+ "./image": "./dist/image.js",
35
+ "./video": "./dist/video.js",
36
+ "./feed": "./dist/feed.js",
37
+ "./package.json": "./package.json"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "scripts": {
43
+ "dev": "vp pack --watch",
44
+ "pack": "vp pack",
45
+ "clean": "rimraf dist node_modules"
46
+ },
47
+ "dependencies": {
48
+ "@rixl/media-core": "1.0.1",
49
+ "@rixl/media-lit": "1.0.1"
50
+ },
51
+ "peerDependencies": {
52
+ "vue": ">=3.3.0"
53
+ }
54
+ }