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.
Files changed (50) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +7 -0
  3. package/dist/runtime/file-preview/components/renderers/audio-renderer.vue +1 -1
  4. package/dist/runtime/file-preview/components/renderers/video-renderer.vue +26 -84
  5. package/dist/runtime/i18n/locales/en.js +5 -0
  6. package/dist/runtime/i18n/locales/fr.js +5 -0
  7. package/dist/runtime/i18n/types.d.ts +7 -0
  8. package/dist/runtime/{file-preview/components/renderers → media/components}/media-controls.vue +3 -3
  9. package/dist/runtime/media/components/video-player.d.vue.ts +58 -0
  10. package/dist/runtime/media/components/video-player.vue +155 -0
  11. package/dist/runtime/media/components/video-player.vue.d.ts +58 -0
  12. package/dist/runtime/media/index.d.ts +1 -0
  13. package/dist/runtime/media/index.js +0 -0
  14. package/dist/runtime/media/types/index.d.ts +1 -0
  15. package/dist/runtime/media/types/index.js +0 -0
  16. package/dist/runtime/media/types/media.d.ts +8 -0
  17. package/dist/runtime/media/types/media.js +0 -0
  18. package/dist/runtime/table/components/grid/grid-renderer.vue +33 -8
  19. package/dist/runtime/table/components/table/table-group-header.d.vue.ts +9 -0
  20. package/dist/runtime/table/components/table/table-group-header.vue +77 -0
  21. package/dist/runtime/table/components/table/table-group-header.vue.d.ts +9 -0
  22. package/dist/runtime/table/components/table/table-renderer.vue +12 -7
  23. package/dist/runtime/table/components/table/table-row.d.vue.ts +4 -1
  24. package/dist/runtime/table/components/table/table-row.vue +16 -1
  25. package/dist/runtime/table/components/table/table-row.vue.d.ts +4 -1
  26. package/dist/runtime/table/components/table/table-selection-control.d.vue.ts +2 -0
  27. package/dist/runtime/table/components/table/table-selection-control.vue +3 -2
  28. package/dist/runtime/table/components/table/table-selection-control.vue.d.ts +2 -0
  29. package/dist/runtime/table/composables/use-table-grid.d.ts +7 -3
  30. package/dist/runtime/table/composables/use-table-grid.js +27 -2
  31. package/dist/runtime/table/composables/use-table-groups.d.ts +28 -0
  32. package/dist/runtime/table/composables/use-table-groups.js +133 -0
  33. package/dist/runtime/table/composables/use-table-internals.d.ts +42 -0
  34. package/dist/runtime/table/composables/use-table-internals.js +9 -0
  35. package/dist/runtime/table/composables/use-table-selection.d.ts +5 -0
  36. package/dist/runtime/table/composables/use-table-selection.js +13 -3
  37. package/dist/runtime/table/composables/use-table-tree.js +7 -5
  38. package/dist/runtime/table/constants/row-motion.d.ts +8 -0
  39. package/dist/runtime/table/constants/row-motion.js +5 -0
  40. package/dist/runtime/table/types/groups.d.ts +44 -0
  41. package/dist/runtime/table/types/groups.js +0 -0
  42. package/dist/runtime/table/types/index.d.ts +1 -0
  43. package/dist/runtime/table/types/layout.d.ts +5 -0
  44. package/dist/runtime/table/utils/groups.d.ts +14 -0
  45. package/dist/runtime/table/utils/groups.js +55 -0
  46. package/dist/runtime/table/utils/index.d.ts +1 -0
  47. package/dist/runtime/table/utils/index.js +1 -0
  48. package/package.json +16 -1
  49. /package/dist/runtime/{file-preview/components/renderers → media/components}/media-controls.d.vue.ts +0 -0
  50. /package/dist/runtime/{file-preview/components/renderers → media/components}/media-controls.vue.d.ts +0 -0
package/dist/module.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "configKey": "nuxtUiTools",
6
6
  "docs": "https://ui.nuxt.com/docs/getting-started/installation/nuxt",
7
7
  "name": "nuxt-ui-tools",
8
- "version": "1.27.5",
8
+ "version": "1.29.0",
9
9
  "builder": {
10
10
  "@nuxt/module-builder": "1.0.3",
11
11
  "unbuild": "unknown"
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 UIcon from "@nuxt/ui/components/Icon.vue";
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
- import MediaControls from "./media-controls.vue";
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 frame = useTemplateRef("frame");
19
- const video = useTemplateRef("video");
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
- { label: t("filePreview.fields.duration"), value: formatFilePreviewDuration(element.duration) },
19
+ {
20
+ label: t("filePreview.fields.duration"),
21
+ value: formatFilePreviewDuration(metadata.duration)
22
+ },
47
23
  {
48
24
  label: t("filePreview.fields.dimensions"),
49
- value: `${element.videoWidth} \xD7 ${element.videoHeight} px`
25
+ value: `${metadata.width} \xD7 ${metadata.height} px`
50
26
  }
51
27
  ]);
52
28
  }
53
- function handleError() {
54
- emit("error", { message: video.value?.error?.message || void 0, reason: "decode" });
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") controls.muted.value = !controls.muted.value;
58
- else if (event.key === "f") void fullscreen.toggle();
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
- <div
65
- ref="frame"
66
- :class="['absolute inset-0 overflow-hidden bg-black', showControls ? '' : 'cursor-none']"
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
- @pointermove="wake"
69
- >
70
- <video
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;
@@ -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 { useUiToolsLocale } from "../../../i18n/use-locale";
7
- import { formatFilePreviewDuration } from "../../utils/format";
8
- import FilePreviewButton from "../shell/file-preview-button.vue";
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
@@ -0,0 +1,8 @@
1
+ /** A text track of a video: subtitles or captions, in one language. */
2
+ export interface VideoPlayerTrack {
3
+ src: string;
4
+ srcLang: string;
5
+ label: string;
6
+ kind?: TextTrackKind;
7
+ default?: boolean;
8
+ }
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
- <div
279
- v-for="(row, index) in tableRows"
280
- :key="rowId(row, index)"
281
- :class="mergeDataListUiClass('nut-dl-grid__item min-w-0', void 0, ui?.item)"
282
- :style="{ gridColumn }"
283
- >
284
- <GridCard :row-index="index" />
285
- </div>
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;