nuxt-ui-tools 1.27.5 → 1.29.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/dist/module.json +1 -1
- package/dist/module.mjs +7 -0
- package/dist/runtime/file-preview/components/renderers/audio-renderer.vue +1 -1
- package/dist/runtime/file-preview/components/renderers/video-renderer.vue +26 -84
- package/dist/runtime/i18n/locales/en.js +5 -0
- package/dist/runtime/i18n/locales/fr.js +5 -0
- package/dist/runtime/i18n/types.d.ts +7 -0
- package/dist/runtime/{file-preview/components/renderers → media/components}/media-controls.vue +3 -3
- package/dist/runtime/media/components/video-player.d.vue.ts +58 -0
- package/dist/runtime/media/components/video-player.vue +155 -0
- package/dist/runtime/media/components/video-player.vue.d.ts +58 -0
- package/dist/runtime/media/index.d.ts +1 -0
- package/dist/runtime/media/index.js +0 -0
- package/dist/runtime/media/types/index.d.ts +1 -0
- package/dist/runtime/media/types/index.js +0 -0
- package/dist/runtime/media/types/media.d.ts +8 -0
- package/dist/runtime/media/types/media.js +0 -0
- package/dist/runtime/table/components/grid/grid-renderer.vue +33 -8
- package/dist/runtime/table/components/table/table-group-header.d.vue.ts +9 -0
- package/dist/runtime/table/components/table/table-group-header.vue +77 -0
- package/dist/runtime/table/components/table/table-group-header.vue.d.ts +9 -0
- package/dist/runtime/table/components/table/table-renderer.vue +12 -7
- package/dist/runtime/table/components/table/table-row.d.vue.ts +4 -1
- package/dist/runtime/table/components/table/table-row.vue +16 -1
- package/dist/runtime/table/components/table/table-row.vue.d.ts +4 -1
- package/dist/runtime/table/components/table/table-selection-control.d.vue.ts +2 -0
- package/dist/runtime/table/components/table/table-selection-control.vue +3 -2
- package/dist/runtime/table/components/table/table-selection-control.vue.d.ts +2 -0
- package/dist/runtime/table/composables/use-table-grid.d.ts +7 -3
- package/dist/runtime/table/composables/use-table-grid.js +27 -2
- package/dist/runtime/table/composables/use-table-groups.d.ts +28 -0
- package/dist/runtime/table/composables/use-table-groups.js +133 -0
- package/dist/runtime/table/composables/use-table-internals.d.ts +42 -0
- package/dist/runtime/table/composables/use-table-internals.js +9 -0
- package/dist/runtime/table/composables/use-table-selection.d.ts +5 -0
- package/dist/runtime/table/composables/use-table-selection.js +13 -3
- package/dist/runtime/table/composables/use-table-tree.js +7 -5
- package/dist/runtime/table/constants/row-motion.d.ts +8 -0
- package/dist/runtime/table/constants/row-motion.js +5 -0
- package/dist/runtime/table/types/groups.d.ts +44 -0
- package/dist/runtime/table/types/groups.js +0 -0
- package/dist/runtime/table/types/index.d.ts +1 -0
- package/dist/runtime/table/types/layout.d.ts +5 -0
- package/dist/runtime/table/utils/groups.d.ts +14 -0
- package/dist/runtime/table/utils/groups.js +55 -0
- package/dist/runtime/table/utils/index.d.ts +1 -0
- package/dist/runtime/table/utils/index.js +1 -0
- package/package.json +16 -1
- /package/dist/runtime/{file-preview/components/renderers → media/components}/media-controls.d.vue.ts +0 -0
- /package/dist/runtime/{file-preview/components/renderers → media/components}/media-controls.vue.d.ts +0 -0
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -137,6 +137,12 @@ function getPublicComponents(runtimeDir, options) {
|
|
|
137
137
|
global: options.global,
|
|
138
138
|
name: `${prefix}FilePreviewTools`
|
|
139
139
|
},
|
|
140
|
+
// Media
|
|
141
|
+
{
|
|
142
|
+
filePath: `${runtimeDir}/media/components/video-player.vue`,
|
|
143
|
+
global: options.global,
|
|
144
|
+
name: `${prefix}VideoPlayer`
|
|
145
|
+
},
|
|
140
146
|
// Form
|
|
141
147
|
{
|
|
142
148
|
filePath: `${runtimeDir}/form/components/root/form.vue`,
|
|
@@ -427,6 +433,7 @@ const publicRuntimeDomains = [
|
|
|
427
433
|
"file-preview",
|
|
428
434
|
"form",
|
|
429
435
|
"i18n",
|
|
436
|
+
"media",
|
|
430
437
|
"query-prefetch",
|
|
431
438
|
"query-state",
|
|
432
439
|
"shared",
|
|
@@ -3,9 +3,9 @@ import UIcon from "@nuxt/ui/components/Icon.vue";
|
|
|
3
3
|
import { useMediaControls } from "@vueuse/core";
|
|
4
4
|
import { computed, useTemplateRef } from "vue";
|
|
5
5
|
import { useUiToolsLocale } from "../../../i18n/use-locale";
|
|
6
|
+
import MediaControls from "../../../media/components/media-controls.vue";
|
|
6
7
|
import { onFilePreviewKey } from "../../composables/use-file-preview-shell";
|
|
7
8
|
import { formatFilePreviewDuration } from "../../utils/format";
|
|
8
|
-
import MediaControls from "./media-controls.vue";
|
|
9
9
|
const BARS = Array.from(
|
|
10
10
|
{ length: 28 },
|
|
11
11
|
(_, index) => 18 + Math.abs(Math.sin(index * 1.3) * Math.cos(index * 0.4)) * 62
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import
|
|
3
|
-
import { useFullscreen, useMediaControls, useTimeoutFn } from "@vueuse/core";
|
|
4
|
-
import { computed, ref, useTemplateRef } from "vue";
|
|
2
|
+
import { useTemplateRef } from "vue";
|
|
5
3
|
import { useUiToolsLocale } from "../../../i18n/use-locale";
|
|
4
|
+
import VideoPlayer from "../../../media/components/video-player.vue";
|
|
6
5
|
import { onFilePreviewKey, useFilePreviewShell } from "../../composables/use-file-preview-shell";
|
|
7
6
|
import { formatFilePreviewDuration } from "../../utils/format";
|
|
8
|
-
|
|
9
|
-
const props = defineProps({
|
|
7
|
+
defineProps({
|
|
10
8
|
file: { type: Object, required: true },
|
|
11
9
|
url: { type: String, required: true },
|
|
12
10
|
blob: { type: null, required: true },
|
|
@@ -15,96 +13,40 @@ const props = defineProps({
|
|
|
15
13
|
const emit = defineEmits(["ready", "error"]);
|
|
16
14
|
const { t } = useUiToolsLocale();
|
|
17
15
|
const shell = useFilePreviewShell();
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
const controls = useMediaControls(video, {
|
|
21
|
-
tracks: () => (props.file.file.tracks ?? []).map((track) => ({
|
|
22
|
-
default: track.default,
|
|
23
|
-
kind: track.kind ?? "subtitles",
|
|
24
|
-
label: track.label,
|
|
25
|
-
src: track.src,
|
|
26
|
-
srcLang: track.srcLang
|
|
27
|
-
}))
|
|
28
|
-
});
|
|
29
|
-
const fullscreen = useFullscreen(frame);
|
|
30
|
-
const active = ref(true);
|
|
31
|
-
const rest = useTimeoutFn(() => active.value = false, 2500);
|
|
32
|
-
const started = computed(() => controls.playing.value || controls.currentTime.value > 0.05);
|
|
33
|
-
const showControls = computed(() => !controls.playing.value || active.value);
|
|
34
|
-
function wake() {
|
|
35
|
-
active.value = true;
|
|
36
|
-
rest.start();
|
|
37
|
-
}
|
|
38
|
-
function toggle() {
|
|
39
|
-
controls.playing.value = !controls.playing.value;
|
|
40
|
-
wake();
|
|
41
|
-
}
|
|
42
|
-
function handleMetadata() {
|
|
43
|
-
const element = video.value;
|
|
44
|
-
if (!element) return;
|
|
16
|
+
const player = useTemplateRef("player");
|
|
17
|
+
function handleReady(metadata) {
|
|
45
18
|
emit("ready", [
|
|
46
|
-
{
|
|
19
|
+
{
|
|
20
|
+
label: t("filePreview.fields.duration"),
|
|
21
|
+
value: formatFilePreviewDuration(metadata.duration)
|
|
22
|
+
},
|
|
47
23
|
{
|
|
48
24
|
label: t("filePreview.fields.dimensions"),
|
|
49
|
-
value: `${
|
|
25
|
+
value: `${metadata.width} \xD7 ${metadata.height} px`
|
|
50
26
|
}
|
|
51
27
|
]);
|
|
52
28
|
}
|
|
53
|
-
function handleError() {
|
|
54
|
-
emit("error", { message:
|
|
29
|
+
function handleError(error) {
|
|
30
|
+
emit("error", { message: error.message, reason: "decode" });
|
|
55
31
|
}
|
|
56
32
|
onFilePreviewKey([" ", "k", "m", "f"], (event) => {
|
|
57
|
-
if (event.key === "m")
|
|
58
|
-
else if (event.key === "f")
|
|
59
|
-
else toggle();
|
|
33
|
+
if (event.key === "m") player.value?.toggleMute();
|
|
34
|
+
else if (event.key === "f") player.value?.toggleFullscreen();
|
|
35
|
+
else player.value?.toggle();
|
|
60
36
|
});
|
|
61
37
|
</script>
|
|
62
38
|
|
|
63
39
|
<template>
|
|
64
|
-
<
|
|
65
|
-
ref="
|
|
66
|
-
:
|
|
40
|
+
<VideoPlayer
|
|
41
|
+
ref="player"
|
|
42
|
+
:src="url"
|
|
43
|
+
:poster="file.file.poster"
|
|
44
|
+
:tracks="file.file.tracks"
|
|
45
|
+
:keyboard="false"
|
|
46
|
+
:compact="shell.narrow.value"
|
|
47
|
+
class="absolute inset-0"
|
|
67
48
|
data-file-preview-video=""
|
|
68
|
-
@
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
ref="video"
|
|
72
|
-
:src="url"
|
|
73
|
-
:poster="file.file.poster"
|
|
74
|
-
preload="metadata"
|
|
75
|
-
playsinline
|
|
76
|
-
class="size-full object-contain"
|
|
77
|
-
@click="toggle"
|
|
78
|
-
@dblclick="fullscreen.toggle()"
|
|
79
|
-
@loadedmetadata="handleMetadata"
|
|
80
|
-
@error="handleError"
|
|
81
|
-
/>
|
|
82
|
-
|
|
83
|
-
<button
|
|
84
|
-
v-if="!started"
|
|
85
|
-
type="button"
|
|
86
|
-
class="absolute top-1/2 left-1/2 grid size-17 -translate-1/2 place-items-center rounded-full bg-black/55 ps-1 text-white transition hover:bg-black/70"
|
|
87
|
-
:aria-label="t('filePreview.media.play')"
|
|
88
|
-
data-file-preview-big-play=""
|
|
89
|
-
@click="toggle"
|
|
90
|
-
>
|
|
91
|
-
<UIcon name="i-lucide-play" class="size-7" />
|
|
92
|
-
</button>
|
|
93
|
-
<span
|
|
94
|
-
v-else-if="controls.waiting.value"
|
|
95
|
-
class="pointer-events-none absolute top-1/2 left-1/2 size-10 -translate-1/2 animate-spin rounded-full border-2 border-white/30 border-t-white"
|
|
96
|
-
aria-hidden="true"
|
|
97
|
-
/>
|
|
98
|
-
|
|
99
|
-
<MediaControls
|
|
100
|
-
:class="[
|
|
101
|
-
'absolute inset-x-0 bottom-0 transition-opacity duration-200 motion-reduce:transition-none',
|
|
102
|
-
showControls ? 'opacity-100' : 'pointer-events-none opacity-0'
|
|
103
|
-
]"
|
|
104
|
-
:controls="controls"
|
|
105
|
-
:fullscreen="fullscreen"
|
|
106
|
-
:compact="shell.narrow.value"
|
|
107
|
-
variant="video"
|
|
108
|
-
/>
|
|
109
|
-
</div>
|
|
49
|
+
@ready="handleReady"
|
|
50
|
+
@error="handleError"
|
|
51
|
+
/>
|
|
110
52
|
</template>
|
|
@@ -748,6 +748,11 @@ export default defineUiToolsLocale({
|
|
|
748
748
|
label: "Actions for {label}",
|
|
749
749
|
labelAt: "Actions for row {position}"
|
|
750
750
|
},
|
|
751
|
+
groups: {
|
|
752
|
+
empty: "Other",
|
|
753
|
+
select: "Select the whole group",
|
|
754
|
+
toggle: "Fold or unfold the group"
|
|
755
|
+
},
|
|
751
756
|
selection: {
|
|
752
757
|
selectAll: "Select all rows",
|
|
753
758
|
selectRow: "Select {label}",
|
|
@@ -748,6 +748,11 @@ export default defineUiToolsLocale({
|
|
|
748
748
|
label: "Actions pour {label}",
|
|
749
749
|
labelAt: "Actions pour la ligne {position}"
|
|
750
750
|
},
|
|
751
|
+
groups: {
|
|
752
|
+
empty: "Autres",
|
|
753
|
+
select: "S\xE9lectionner tout le groupe",
|
|
754
|
+
toggle: "Replier ou d\xE9plier le groupe"
|
|
755
|
+
},
|
|
751
756
|
selection: {
|
|
752
757
|
selectAll: "S\xE9lectionner toutes les lignes",
|
|
753
758
|
selectRow: "S\xE9lectionner {label}",
|
|
@@ -23,6 +23,13 @@ export interface UiToolsTableMessages {
|
|
|
23
23
|
firstPage: string;
|
|
24
24
|
lastPage: string;
|
|
25
25
|
};
|
|
26
|
+
/** A row group's header. */
|
|
27
|
+
groups: {
|
|
28
|
+
/** The label of the group holding rows with no value. */
|
|
29
|
+
empty: string;
|
|
30
|
+
select: string;
|
|
31
|
+
toggle: string;
|
|
32
|
+
};
|
|
26
33
|
/** Accessible names of the selection checkboxes. */
|
|
27
34
|
selection: {
|
|
28
35
|
selectAll: string;
|
package/dist/runtime/{file-preview/components/renderers → media/components}/media-controls.vue
RENAMED
|
@@ -3,9 +3,9 @@ import UButton from "@nuxt/ui/components/Button.vue";
|
|
|
3
3
|
import UDropdownMenu from "@nuxt/ui/components/DropdownMenu.vue";
|
|
4
4
|
import USlider from "@nuxt/ui/components/Slider.vue";
|
|
5
5
|
import { computed } from "vue";
|
|
6
|
-
import
|
|
7
|
-
import { formatFilePreviewDuration } from "../../utils/format";
|
|
8
|
-
import
|
|
6
|
+
import FilePreviewButton from "../../file-preview/components/shell/file-preview-button.vue";
|
|
7
|
+
import { formatFilePreviewDuration } from "../../file-preview/utils/format";
|
|
8
|
+
import { useUiToolsLocale } from "../../i18n/use-locale";
|
|
9
9
|
const RATES = [0.5, 0.75, 1, 1.25, 1.5, 2];
|
|
10
10
|
const props = defineProps({
|
|
11
11
|
controls: { type: null, required: true },
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { VideoPlayerTrack } from '../types/media.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
src: string;
|
|
4
|
+
poster?: string;
|
|
5
|
+
/** Read by assistive technology, and shown over the poster before playback starts. */
|
|
6
|
+
title?: string;
|
|
7
|
+
tracks?: readonly VideoPlayerTrack[];
|
|
8
|
+
/** Space or K plays and pauses, M mutes, F toggles full screen while the player has focus. */
|
|
9
|
+
keyboard?: boolean;
|
|
10
|
+
/** Drops the volume slider; by default the player compacts itself below 480 px. */
|
|
11
|
+
compact?: boolean;
|
|
12
|
+
};
|
|
13
|
+
declare function toggle(): void;
|
|
14
|
+
declare function toggleMute(): void;
|
|
15
|
+
declare function toggleFullscreen(): void;
|
|
16
|
+
declare var __VLS_12: {};
|
|
17
|
+
type __VLS_Slots = {} & {
|
|
18
|
+
caption?: (props: typeof __VLS_12) => any;
|
|
19
|
+
};
|
|
20
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
21
|
+
controls: import("@vueuse/core").UseMediaControlsReturn;
|
|
22
|
+
fullscreen: import("@vueuse/core").UseFullscreenReturn;
|
|
23
|
+
toggle: typeof toggle;
|
|
24
|
+
toggleMute: typeof toggleMute;
|
|
25
|
+
toggleFullscreen: typeof toggleFullscreen;
|
|
26
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
27
|
+
ready: (metadata: {
|
|
28
|
+
duration: number;
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
}) => any;
|
|
32
|
+
error: (error: {
|
|
33
|
+
message?: string;
|
|
34
|
+
}) => any;
|
|
35
|
+
ended: () => any;
|
|
36
|
+
pause: () => any;
|
|
37
|
+
play: () => any;
|
|
38
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
39
|
+
onReady?: ((metadata: {
|
|
40
|
+
duration: number;
|
|
41
|
+
width: number;
|
|
42
|
+
height: number;
|
|
43
|
+
}) => any) | undefined;
|
|
44
|
+
onError?: ((error: {
|
|
45
|
+
message?: string;
|
|
46
|
+
}) => any) | undefined;
|
|
47
|
+
onEnded?: (() => any) | undefined;
|
|
48
|
+
onPause?: (() => any) | undefined;
|
|
49
|
+
onPlay?: (() => any) | undefined;
|
|
50
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
51
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
52
|
+
declare const _default: typeof __VLS_export;
|
|
53
|
+
export default _default;
|
|
54
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
55
|
+
new (): {
|
|
56
|
+
$slots: S;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import UIcon from "@nuxt/ui/components/Icon.vue";
|
|
3
|
+
import { useElementSize, useFullscreen, useMediaControls, useTimeoutFn } from "@vueuse/core";
|
|
4
|
+
import { computed, ref, useTemplateRef, watch } from "vue";
|
|
5
|
+
import { useUiToolsLocale } from "../../i18n/use-locale";
|
|
6
|
+
import MediaControls from "./media-controls.vue";
|
|
7
|
+
const {
|
|
8
|
+
src,
|
|
9
|
+
poster = void 0,
|
|
10
|
+
title = void 0,
|
|
11
|
+
tracks = [],
|
|
12
|
+
keyboard = true,
|
|
13
|
+
compact = void 0
|
|
14
|
+
} = defineProps({
|
|
15
|
+
src: { type: String, required: true },
|
|
16
|
+
poster: { type: String, required: false },
|
|
17
|
+
title: { type: String, required: false },
|
|
18
|
+
tracks: { type: Array, required: false },
|
|
19
|
+
keyboard: { type: Boolean, required: false },
|
|
20
|
+
compact: { type: Boolean, required: false }
|
|
21
|
+
});
|
|
22
|
+
const emit = defineEmits(["ready", "error", "play", "pause", "ended"]);
|
|
23
|
+
const { t } = useUiToolsLocale();
|
|
24
|
+
const frame = useTemplateRef("frame");
|
|
25
|
+
const video = useTemplateRef("video");
|
|
26
|
+
const controls = useMediaControls(video, {
|
|
27
|
+
tracks: () => tracks.map((track) => ({
|
|
28
|
+
default: track.default,
|
|
29
|
+
kind: track.kind ?? "subtitles",
|
|
30
|
+
label: track.label,
|
|
31
|
+
src: track.src,
|
|
32
|
+
srcLang: track.srcLang
|
|
33
|
+
}))
|
|
34
|
+
});
|
|
35
|
+
const fullscreen = useFullscreen(frame);
|
|
36
|
+
const { width } = useElementSize(frame);
|
|
37
|
+
const active = ref(true);
|
|
38
|
+
const rest = useTimeoutFn(() => active.value = false, 2500);
|
|
39
|
+
const started = computed(() => controls.playing.value || controls.currentTime.value > 0.05);
|
|
40
|
+
const showControls = computed(() => !controls.playing.value || active.value);
|
|
41
|
+
const narrow = computed(() => compact ?? (width.value > 0 && width.value < 480));
|
|
42
|
+
watch(controls.playing, (playing) => playing ? emit("play") : emit("pause"));
|
|
43
|
+
watch(controls.ended, (ended) => ended && emit("ended"));
|
|
44
|
+
function wake() {
|
|
45
|
+
active.value = true;
|
|
46
|
+
rest.start();
|
|
47
|
+
}
|
|
48
|
+
function toggle() {
|
|
49
|
+
controls.playing.value = !controls.playing.value;
|
|
50
|
+
wake();
|
|
51
|
+
}
|
|
52
|
+
function toggleMute() {
|
|
53
|
+
controls.muted.value = !controls.muted.value;
|
|
54
|
+
wake();
|
|
55
|
+
}
|
|
56
|
+
function toggleFullscreen() {
|
|
57
|
+
void fullscreen.toggle();
|
|
58
|
+
}
|
|
59
|
+
function handleMetadata() {
|
|
60
|
+
const element = video.value;
|
|
61
|
+
if (!element) return;
|
|
62
|
+
emit("ready", {
|
|
63
|
+
duration: element.duration,
|
|
64
|
+
width: element.videoWidth,
|
|
65
|
+
height: element.videoHeight
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function handleError() {
|
|
69
|
+
emit("error", { message: video.value?.error?.message || void 0 });
|
|
70
|
+
}
|
|
71
|
+
function handleKey(event) {
|
|
72
|
+
if (!keyboard || event.target !== frame.value) return;
|
|
73
|
+
if (event.key === "m") toggleMute();
|
|
74
|
+
else if (event.key === "f") toggleFullscreen();
|
|
75
|
+
else if (event.key === " " || event.key === "k") toggle();
|
|
76
|
+
else return;
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
}
|
|
79
|
+
defineExpose({ controls, fullscreen, toggle, toggleMute, toggleFullscreen });
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<template>
|
|
83
|
+
<div
|
|
84
|
+
ref="frame"
|
|
85
|
+
:class="[
|
|
86
|
+
'group/video relative overflow-hidden bg-black outline-none focus-visible:ring-2 focus-visible:ring-primary/60',
|
|
87
|
+
showControls ? '' : 'cursor-none'
|
|
88
|
+
]"
|
|
89
|
+
:tabindex="keyboard ? 0 : void 0"
|
|
90
|
+
:aria-label="title"
|
|
91
|
+
role="region"
|
|
92
|
+
data-video-player=""
|
|
93
|
+
@pointermove="wake"
|
|
94
|
+
@keydown="handleKey"
|
|
95
|
+
>
|
|
96
|
+
<video
|
|
97
|
+
ref="video"
|
|
98
|
+
:src="src"
|
|
99
|
+
:poster="poster"
|
|
100
|
+
:title="title"
|
|
101
|
+
preload="metadata"
|
|
102
|
+
playsinline
|
|
103
|
+
class="size-full object-contain"
|
|
104
|
+
@click="toggle"
|
|
105
|
+
@dblclick="toggleFullscreen"
|
|
106
|
+
@loadedmetadata="handleMetadata"
|
|
107
|
+
@error="handleError"
|
|
108
|
+
/>
|
|
109
|
+
|
|
110
|
+
<Transition
|
|
111
|
+
enter-active-class="transition duration-200 ease-out motion-reduce:transition-none"
|
|
112
|
+
enter-from-class="opacity-0"
|
|
113
|
+
leave-active-class="transition duration-150 ease-in motion-reduce:transition-none"
|
|
114
|
+
leave-to-class="opacity-0"
|
|
115
|
+
>
|
|
116
|
+
<button
|
|
117
|
+
v-if="!started"
|
|
118
|
+
type="button"
|
|
119
|
+
class="absolute inset-0 grid place-items-center bg-linear-to-t from-black/45 via-black/10 to-transparent text-white"
|
|
120
|
+
:aria-label="t('filePreview.media.play')"
|
|
121
|
+
data-video-player-start=""
|
|
122
|
+
@click="toggle"
|
|
123
|
+
>
|
|
124
|
+
<span
|
|
125
|
+
class="grid size-16 place-items-center rounded-full bg-white/95 ps-1 text-black shadow-[0_12px_32px_-12px_rgba(0,0,0,0.6)] transition duration-200 ease-out group-hover/video:scale-105 motion-reduce:transition-none"
|
|
126
|
+
>
|
|
127
|
+
<UIcon name="i-lucide-play" class="size-7 fill-current" />
|
|
128
|
+
</span>
|
|
129
|
+
<span
|
|
130
|
+
v-if="title || $slots.caption"
|
|
131
|
+
class="absolute inset-x-0 bottom-0 truncate px-5 pb-4 text-start text-sm font-medium text-white/95"
|
|
132
|
+
>
|
|
133
|
+
<slot name="caption">{{ title }}</slot>
|
|
134
|
+
</span>
|
|
135
|
+
</button>
|
|
136
|
+
</Transition>
|
|
137
|
+
<span
|
|
138
|
+
v-if="started && controls.waiting.value"
|
|
139
|
+
class="pointer-events-none absolute top-1/2 left-1/2 size-10 -translate-1/2 animate-spin rounded-full border-2 border-white/30 border-t-white"
|
|
140
|
+
aria-hidden="true"
|
|
141
|
+
/>
|
|
142
|
+
|
|
143
|
+
<MediaControls
|
|
144
|
+
v-if="started"
|
|
145
|
+
:class="[
|
|
146
|
+
'absolute inset-x-0 bottom-0 transition-opacity duration-200 motion-reduce:transition-none',
|
|
147
|
+
showControls ? 'opacity-100' : 'pointer-events-none opacity-0'
|
|
148
|
+
]"
|
|
149
|
+
:controls="controls"
|
|
150
|
+
:fullscreen="fullscreen"
|
|
151
|
+
:compact="narrow"
|
|
152
|
+
variant="video"
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
</template>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { VideoPlayerTrack } from '../types/media.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
src: string;
|
|
4
|
+
poster?: string;
|
|
5
|
+
/** Read by assistive technology, and shown over the poster before playback starts. */
|
|
6
|
+
title?: string;
|
|
7
|
+
tracks?: readonly VideoPlayerTrack[];
|
|
8
|
+
/** Space or K plays and pauses, M mutes, F toggles full screen while the player has focus. */
|
|
9
|
+
keyboard?: boolean;
|
|
10
|
+
/** Drops the volume slider; by default the player compacts itself below 480 px. */
|
|
11
|
+
compact?: boolean;
|
|
12
|
+
};
|
|
13
|
+
declare function toggle(): void;
|
|
14
|
+
declare function toggleMute(): void;
|
|
15
|
+
declare function toggleFullscreen(): void;
|
|
16
|
+
declare var __VLS_12: {};
|
|
17
|
+
type __VLS_Slots = {} & {
|
|
18
|
+
caption?: (props: typeof __VLS_12) => any;
|
|
19
|
+
};
|
|
20
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
21
|
+
controls: import("@vueuse/core").UseMediaControlsReturn;
|
|
22
|
+
fullscreen: import("@vueuse/core").UseFullscreenReturn;
|
|
23
|
+
toggle: typeof toggle;
|
|
24
|
+
toggleMute: typeof toggleMute;
|
|
25
|
+
toggleFullscreen: typeof toggleFullscreen;
|
|
26
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
27
|
+
ready: (metadata: {
|
|
28
|
+
duration: number;
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
}) => any;
|
|
32
|
+
error: (error: {
|
|
33
|
+
message?: string;
|
|
34
|
+
}) => any;
|
|
35
|
+
ended: () => any;
|
|
36
|
+
pause: () => any;
|
|
37
|
+
play: () => any;
|
|
38
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
39
|
+
onReady?: ((metadata: {
|
|
40
|
+
duration: number;
|
|
41
|
+
width: number;
|
|
42
|
+
height: number;
|
|
43
|
+
}) => any) | undefined;
|
|
44
|
+
onError?: ((error: {
|
|
45
|
+
message?: string;
|
|
46
|
+
}) => any) | undefined;
|
|
47
|
+
onEnded?: (() => any) | undefined;
|
|
48
|
+
onPause?: (() => any) | undefined;
|
|
49
|
+
onPlay?: (() => any) | undefined;
|
|
50
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
51
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
52
|
+
declare const _default: typeof __VLS_export;
|
|
53
|
+
export default _default;
|
|
54
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
55
|
+
new (): {
|
|
56
|
+
$slots: S;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type * from './types';
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type * from './media';
|
|
File without changes
|
|
File without changes
|
|
@@ -11,6 +11,7 @@ import { mergeDataListUiClass, resolveTableRowId } from "../../utils";
|
|
|
11
11
|
import DataListErrorState from "../data-list/data-list-error-state.vue";
|
|
12
12
|
import ProgressLine from "../layout/progress-line.vue";
|
|
13
13
|
import TableEmptyState from "../table/table-empty-state.vue";
|
|
14
|
+
import TableGroupHeader from "../table/table-group-header.vue";
|
|
14
15
|
import OverlayInsetSpacer from "../utils/overlay-inset-spacer.vue";
|
|
15
16
|
import GridCard from "./grid-card.vue";
|
|
16
17
|
import GridSkeleton from "./grid-skeleton.vue";
|
|
@@ -75,12 +76,21 @@ watch(
|
|
|
75
76
|
);
|
|
76
77
|
function getVirtualRowKey(index) {
|
|
77
78
|
const chunk = rowChunks.value[index];
|
|
79
|
+
if (chunk?.group) return `grid-group:${chunk.group.key}`;
|
|
78
80
|
const firstRow = chunk?.rows[0];
|
|
79
81
|
return firstRow ? `grid-row:${resolveTableRowId({ index: chunk.start, row: firstRow, rowKey: internals.schema.value.rowKey })}` : `grid-row:${index}`;
|
|
80
82
|
}
|
|
81
83
|
function rowId(row, index) {
|
|
82
84
|
return resolveTableRowId({ index, row, rowKey: internals.schema.value.rowKey });
|
|
83
85
|
}
|
|
86
|
+
const flowItems = computed(
|
|
87
|
+
() => rowChunks.value.flatMap(
|
|
88
|
+
(chunk) => chunk.group ? [{ group: chunk.group, index: chunk.start, key: `grid-group:${chunk.group.key}` }] : chunk.rows.map((row, offset) => ({
|
|
89
|
+
index: chunk.start + offset,
|
|
90
|
+
key: rowId(row, chunk.start + offset)
|
|
91
|
+
}))
|
|
92
|
+
)
|
|
93
|
+
);
|
|
84
94
|
function scrollToTop() {
|
|
85
95
|
viewportRef.value?.scrollTo({ behavior: "auto", left: 0, top: 0 });
|
|
86
96
|
}
|
|
@@ -258,6 +268,13 @@ function refreshData() {
|
|
|
258
268
|
}"
|
|
259
269
|
:data-index="virtualRow.index"
|
|
260
270
|
>
|
|
271
|
+
<div
|
|
272
|
+
v-if="rowChunks[virtualRow.index]?.group"
|
|
273
|
+
class="nut-dl-grid__group min-w-0"
|
|
274
|
+
:style="{ gridColumn: '1 / -1' }"
|
|
275
|
+
>
|
|
276
|
+
<TableGroupHeader :group="rowChunks[virtualRow.index].group" />
|
|
277
|
+
</div>
|
|
261
278
|
<div
|
|
262
279
|
v-for="(row, offset) in rowChunks[virtualRow.index]?.rows ?? []"
|
|
263
280
|
:key="rowId(row, (rowChunks[virtualRow.index]?.start ?? 0) + offset)"
|
|
@@ -275,14 +292,22 @@ function refreshData() {
|
|
|
275
292
|
:class="mergeDataListUiClass('nut-dl-grid__flow grid', void 0, ui?.flow)"
|
|
276
293
|
:style="{ gridTemplateColumns, gap: `${gap}px` }"
|
|
277
294
|
>
|
|
278
|
-
<
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
295
|
+
<template v-for="item in flowItems" :key="item.key">
|
|
296
|
+
<div
|
|
297
|
+
v-if="item.group"
|
|
298
|
+
class="nut-dl-grid__group min-w-0"
|
|
299
|
+
:style="{ gridColumn: '1 / -1' }"
|
|
300
|
+
>
|
|
301
|
+
<TableGroupHeader :group="item.group" />
|
|
302
|
+
</div>
|
|
303
|
+
<div
|
|
304
|
+
v-else
|
|
305
|
+
:class="mergeDataListUiClass('nut-dl-grid__item min-w-0', void 0, ui?.item)"
|
|
306
|
+
:style="{ gridColumn }"
|
|
307
|
+
>
|
|
308
|
+
<GridCard :row-index="item.index" />
|
|
309
|
+
</div>
|
|
310
|
+
</template>
|
|
286
311
|
</div>
|
|
287
312
|
<OverlayInsetSpacer />
|
|
288
313
|
</div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TableGroup } from '../../types/index.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
group: TableGroup;
|
|
4
|
+
/** Whether the rows beneath it carry checkboxes. */
|
|
5
|
+
selectable?: boolean;
|
|
6
|
+
};
|
|
7
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
8
|
+
declare const _default: typeof __VLS_export;
|
|
9
|
+
export default _default;
|