vgapp 1.4.4 → 1.4.6

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 (123) hide show
  1. package/README.md +270 -270
  2. package/app/langs/en/buttons.json +17 -17
  3. package/app/langs/en/messages.json +36 -36
  4. package/app/langs/ru/buttons.json +17 -17
  5. package/app/langs/ru/messages.json +36 -36
  6. package/app/modules/vgalert/js/vgalert.js +266 -266
  7. package/app/modules/vgalert/scss/_variables.scss +16 -16
  8. package/app/modules/vgalert/scss/vgalert.scss +107 -107
  9. package/app/modules/vgcollapse/scss/_variables.scss +7 -7
  10. package/app/modules/vgcollapse/scss/vgcollapse.scss +5 -5
  11. package/app/modules/vgdropdown/scss/_variables.scss +14 -14
  12. package/app/modules/vgdropdown/scss/vgdropdown.scss +5 -5
  13. package/app/modules/vgdynamictable/index.js +4 -4
  14. package/app/modules/vgdynamictable/js/filters.js +21 -21
  15. package/app/modules/vgdynamictable/js/fixed.js +179 -179
  16. package/app/modules/vgdynamictable/js/options.js +3 -3
  17. package/app/modules/vgdynamictable/js/pagination.js +1 -1
  18. package/app/modules/vgdynamictable/js/search.js +12 -12
  19. package/app/modules/vgdynamictable/js/skeleton.js +27 -19
  20. package/app/modules/vgdynamictable/js/sortable.js +71 -71
  21. package/app/modules/vgdynamictable/js/summary-footer.js +28 -28
  22. package/app/modules/vgdynamictable/js/table-url-state.js +31 -31
  23. package/app/modules/vgdynamictable/js/utils/common.js +48 -48
  24. package/app/modules/vgdynamictable/js/vgdynamictable.js +79 -79
  25. package/app/modules/vgdynamictable/scss/_actions.scss +19 -19
  26. package/app/modules/vgdynamictable/scss/_pagination.scss +5 -5
  27. package/app/modules/vgdynamictable/scss/_skeleton.scss +11 -11
  28. package/app/modules/vgdynamictable/scss/_sortable.scss +28 -28
  29. package/app/modules/vgdynamictable/scss/_table.scss +7 -30
  30. package/app/modules/vgdynamictable/scss/_variables.scss +46 -46
  31. package/app/modules/vgdynamictable/scss/vgdynamictable.scss +28 -36
  32. package/app/modules/vgfilepreview/js/i18n.js +56 -56
  33. package/app/modules/vgfilepreview/js/renderers/image-modal.js +145 -145
  34. package/app/modules/vgfilepreview/js/renderers/image.js +92 -92
  35. package/app/modules/vgfilepreview/js/renderers/index.js +19 -19
  36. package/app/modules/vgfilepreview/js/renderers/office-modal.js +168 -168
  37. package/app/modules/vgfilepreview/js/renderers/office.js +79 -79
  38. package/app/modules/vgfilepreview/js/renderers/pdf-modal.js +260 -260
  39. package/app/modules/vgfilepreview/js/renderers/pdf.js +76 -76
  40. package/app/modules/vgfilepreview/js/renderers/playlist.js +71 -71
  41. package/app/modules/vgfilepreview/js/renderers/text-modal.js +343 -343
  42. package/app/modules/vgfilepreview/js/renderers/text.js +83 -83
  43. package/app/modules/vgfilepreview/js/renderers/video-modal.js +272 -272
  44. package/app/modules/vgfilepreview/js/renderers/video.js +80 -80
  45. package/app/modules/vgfilepreview/js/renderers/zip-modal.js +522 -522
  46. package/app/modules/vgfilepreview/js/renderers/zip.js +89 -89
  47. package/app/modules/vgfilepreview/js/vgfilepreview.js +7 -7
  48. package/app/modules/vgfilepreview/readme.md +68 -68
  49. package/app/modules/vgfilepreview/scss/_variables.scss +115 -115
  50. package/app/modules/vgfilepreview/scss/vgfilepreview.scss +463 -463
  51. package/app/modules/vgfiles/js/droppable.js +20 -9
  52. package/app/modules/vgfiles/js/render.js +153 -153
  53. package/app/modules/vgfiles/js/vgfiles.js +104 -104
  54. package/app/modules/vgfiles/readme.md +123 -123
  55. package/app/modules/vgfiles/scss/_mixins.scss +9 -9
  56. package/app/modules/vgfiles/scss/_variables.scss +41 -41
  57. package/app/modules/vgfiles/scss/vgfiles.scss +161 -161
  58. package/app/modules/vgformsender/js/hideshowpass.js +21 -16
  59. package/app/modules/vgformsender/js/vgformsender.js +4 -6
  60. package/app/modules/vgformsender/readme.md +1 -1
  61. package/app/modules/vgformsender/scss/_variables.scss +5 -5
  62. package/app/modules/vgformsender/scss/vgformsender.scss +42 -42
  63. package/app/modules/vglawcookie/js/vglawcookie.js +11 -8
  64. package/app/modules/vglawcookie/scss/_variables.scss +13 -13
  65. package/app/modules/vglawcookie/scss/vglawcookie.scss +5 -5
  66. package/app/modules/vgmodal/js/vgmodal.interaction.js +216 -216
  67. package/app/modules/vgmodal/js/vgmodal.js +355 -355
  68. package/app/modules/vgmodal/js/vgmodal.minimized.js +227 -227
  69. package/app/modules/vgmodal/scss/_variables.scss +17 -17
  70. package/app/modules/vgmodal/scss/vgmodal.scss +77 -77
  71. package/app/modules/vgnav/js/vgnav.js +180 -180
  72. package/app/modules/vgnav/scss/_breakpoints.scss +36 -36
  73. package/app/modules/vgnav/scss/_hamburger.scss +5 -5
  74. package/app/modules/vgnav/scss/_placement.scss +5 -5
  75. package/app/modules/vgnav/scss/_toggle.scss +5 -5
  76. package/app/modules/vgnav/scss/_variables.scss +26 -26
  77. package/app/modules/vgnav/scss/vgnav.scss +41 -41
  78. package/app/modules/vgnestable/README.md +39 -39
  79. package/app/modules/vgnestable/js/vgnestable.js +358 -358
  80. package/app/modules/vgnestable/scss/_variables.scss +35 -35
  81. package/app/modules/vgnestable/scss/vgnestable.scss +55 -55
  82. package/app/modules/vgrangeslider/js/skins.js +222 -222
  83. package/app/modules/vgrangeslider/js/vgrangeslider.js +704 -704
  84. package/app/modules/vgrangeslider/scss/_variables.scss +55 -55
  85. package/app/modules/vgrangeslider/scss/vgrangeslider.scss +239 -239
  86. package/app/modules/vgrollup/scss/_variables.scss +15 -15
  87. package/app/modules/vgrollup/scss/vgrollup.scss +6 -6
  88. package/app/modules/vgselect/js/vgselect.js +129 -129
  89. package/app/modules/vgselect/readme.md +11 -11
  90. package/app/modules/vgselect/scss/_variables.scss +50 -50
  91. package/app/modules/vgselect/scss/vgselect.scss +48 -48
  92. package/app/modules/vgsidebar/js/vgsidebar.js +79 -79
  93. package/app/modules/vgsidebar/scss/_variables.scss +10 -10
  94. package/app/modules/vgsidebar/scss/vgsidebar.scss +5 -5
  95. package/app/modules/vgtabs/scss/_variables.scss +23 -23
  96. package/app/modules/vgtabs/scss/vgtabs.scss +5 -5
  97. package/app/modules/vgtoast/scss/_variables.scss +13 -13
  98. package/app/modules/vgtoast/scss/vgtoast.scss +5 -5
  99. package/app/modules/vgtooltip/js/vgtooltip.js +107 -51
  100. package/app/modules/vgtooltip/scss/_variables.scss +10 -10
  101. package/app/modules/vgtooltip/scss/vgtooltip.scss +5 -5
  102. package/app/utils/js/components/audio-metadata.js +240 -240
  103. package/app/utils/js/components/backdrop.js +116 -116
  104. package/app/utils/js/components/file-icon.js +109 -109
  105. package/app/utils/js/components/file-preview.js +304 -304
  106. package/app/utils/js/components/sanitize.js +150 -150
  107. package/app/utils/js/components/video-metadata.js +140 -140
  108. package/app/utils/scss/_backdrop.scss +5 -5
  109. package/app/utils/scss/_theme-default.scss +12 -12
  110. package/app/utils/scss/_theme.scss +7 -7
  111. package/app/utils/scss/_tokens.scss +170 -170
  112. package/app/utils/scss/default.scss +9 -9
  113. package/app/utils/scss/functions.scss +21 -21
  114. package/app/utils/scss/index.scss +4 -4
  115. package/app/utils/scss/mixin.scss +16 -16
  116. package/app/utils/scss/variables.scss +105 -105
  117. package/app/vgapp.js +295 -295
  118. package/build/vgapp.css +1 -1
  119. package/build/vgapp.css.map +1 -1
  120. package/build/vgapp.js +1 -1
  121. package/build/vgapp.js.map +1 -1
  122. package/index.scss +20 -20
  123. package/package.json +4 -2
@@ -1,150 +1,150 @@
1
- const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
2
- const DEFAULT_ALLOWED_SVG_TAGS = new Set([
3
- "svg",
4
- "g",
5
- "path",
6
- "line",
7
- "polyline",
8
- "polygon",
9
- "circle",
10
- "ellipse",
11
- "rect",
12
- "defs",
13
- "symbol",
14
- "title",
15
- "use"
16
- ]);
17
- const DEFAULT_ALLOWED_SVG_ATTRS = new Set([
18
- "xmlns",
19
- "viewbox",
20
- "width",
21
- "height",
22
- "fill",
23
- "stroke",
24
- "stroke-width",
25
- "stroke-linecap",
26
- "stroke-linejoin",
27
- "stroke-miterlimit",
28
- "stroke-dasharray",
29
- "stroke-dashoffset",
30
- "opacity",
31
- "transform",
32
- "class",
33
- "x",
34
- "y",
35
- "x1",
36
- "y1",
37
- "x2",
38
- "y2",
39
- "cx",
40
- "cy",
41
- "r",
42
- "rx",
43
- "ry",
44
- "points",
45
- "d",
46
- "role",
47
- "focusable",
48
- "aria-hidden",
49
- "href",
50
- "xlink:href"
51
- ]);
52
-
53
- function escapeHtml(value) {
54
- return String(value)
55
- .replace(/&/g, "&")
56
- .replace(/</g, "&lt;")
57
- .replace(/>/g, "&gt;")
58
- .replace(/"/g, "&quot;")
59
- .replace(/'/g, "&#39;");
60
- }
61
-
62
- function sanitizeSvgNode(node, allowedTags = DEFAULT_ALLOWED_SVG_TAGS, allowedAttrs = DEFAULT_ALLOWED_SVG_ATTRS) {
63
- if (!node) {
64
- return null;
65
- }
66
-
67
- if (node.nodeType === Node.TEXT_NODE) {
68
- return document.createTextNode(node.textContent || "");
69
- }
70
-
71
- if (node.nodeType !== Node.ELEMENT_NODE) {
72
- return null;
73
- }
74
-
75
- const tagName = node.tagName.toLowerCase();
76
- if (!allowedTags.has(tagName)) {
77
- return null;
78
- }
79
-
80
- const safeNode = document.createElementNS(SVG_NAMESPACE, tagName);
81
-
82
- Array.from(node.attributes || []).forEach((attr) => {
83
- const name = attr.name.toLowerCase();
84
- const attrValue = attr.value || "";
85
-
86
- if (!allowedAttrs.has(name) || name.startsWith("on")) {
87
- return;
88
- }
89
-
90
- if ((name === "href" || name === "xlink:href") && attrValue && !attrValue.startsWith("#")) {
91
- return;
92
- }
93
-
94
- safeNode.setAttribute(name, attrValue);
95
- });
96
-
97
- Array.from(node.childNodes || []).forEach((child) => {
98
- const safeChild = sanitizeSvgNode(child, allowedTags, allowedAttrs);
99
- if (safeChild) {
100
- safeNode.append(safeChild);
101
- }
102
- });
103
-
104
- return safeNode;
105
- }
106
-
107
- export function toSafeHtmlString(value, {
108
- allowedSvgTags = DEFAULT_ALLOWED_SVG_TAGS,
109
- allowedSvgAttrs = DEFAULT_ALLOWED_SVG_ATTRS
110
- } = {}) {
111
- if (value === null || value === undefined) {
112
- return "";
113
- }
114
-
115
- const content = String(value).trim();
116
- if (!content) {
117
- return "";
118
- }
119
-
120
- if (!content.includes("<")) {
121
- return escapeHtml(content);
122
- }
123
-
124
- const template = document.createElement("template");
125
- template.innerHTML = content;
126
-
127
- const sanitizedParts = Array.from(template.content.childNodes)
128
- .map((node) => sanitizeSvgNode(node, allowedSvgTags, allowedSvgAttrs))
129
- .filter(Boolean)
130
- .map((node) => {
131
- if (node.nodeType === Node.TEXT_NODE) {
132
- return escapeHtml(node.textContent || "");
133
- }
134
- if (node.nodeType === Node.ELEMENT_NODE) {
135
- return node.outerHTML;
136
- }
137
- return "";
138
- });
139
-
140
- const sanitized = sanitizedParts.join("");
141
- return sanitized || escapeHtml(content);
142
- }
143
-
144
- class Sanitize {
145
- static toSafeHtmlString(value, options) {
146
- return toSafeHtmlString(value, options);
147
- }
148
- }
149
-
150
- export default Sanitize;
1
+ const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
2
+ const DEFAULT_ALLOWED_SVG_TAGS = new Set([
3
+ "svg",
4
+ "g",
5
+ "path",
6
+ "line",
7
+ "polyline",
8
+ "polygon",
9
+ "circle",
10
+ "ellipse",
11
+ "rect",
12
+ "defs",
13
+ "symbol",
14
+ "title",
15
+ "use"
16
+ ]);
17
+ const DEFAULT_ALLOWED_SVG_ATTRS = new Set([
18
+ "xmlns",
19
+ "viewbox",
20
+ "width",
21
+ "height",
22
+ "fill",
23
+ "stroke",
24
+ "stroke-width",
25
+ "stroke-linecap",
26
+ "stroke-linejoin",
27
+ "stroke-miterlimit",
28
+ "stroke-dasharray",
29
+ "stroke-dashoffset",
30
+ "opacity",
31
+ "transform",
32
+ "class",
33
+ "x",
34
+ "y",
35
+ "x1",
36
+ "y1",
37
+ "x2",
38
+ "y2",
39
+ "cx",
40
+ "cy",
41
+ "r",
42
+ "rx",
43
+ "ry",
44
+ "points",
45
+ "d",
46
+ "role",
47
+ "focusable",
48
+ "aria-hidden",
49
+ "href",
50
+ "xlink:href"
51
+ ]);
52
+
53
+ function escapeHtml(value) {
54
+ return String(value)
55
+ .replace(/&/g, "&amp;")
56
+ .replace(/</g, "&lt;")
57
+ .replace(/>/g, "&gt;")
58
+ .replace(/"/g, "&quot;")
59
+ .replace(/'/g, "&#39;");
60
+ }
61
+
62
+ function sanitizeSvgNode(node, allowedTags = DEFAULT_ALLOWED_SVG_TAGS, allowedAttrs = DEFAULT_ALLOWED_SVG_ATTRS) {
63
+ if (!node) {
64
+ return null;
65
+ }
66
+
67
+ if (node.nodeType === Node.TEXT_NODE) {
68
+ return document.createTextNode(node.textContent || "");
69
+ }
70
+
71
+ if (node.nodeType !== Node.ELEMENT_NODE) {
72
+ return null;
73
+ }
74
+
75
+ const tagName = node.tagName.toLowerCase();
76
+ if (!allowedTags.has(tagName)) {
77
+ return null;
78
+ }
79
+
80
+ const safeNode = document.createElementNS(SVG_NAMESPACE, tagName);
81
+
82
+ Array.from(node.attributes || []).forEach((attr) => {
83
+ const name = attr.name.toLowerCase();
84
+ const attrValue = attr.value || "";
85
+
86
+ if (!allowedAttrs.has(name) || name.startsWith("on")) {
87
+ return;
88
+ }
89
+
90
+ if ((name === "href" || name === "xlink:href") && attrValue && !attrValue.startsWith("#")) {
91
+ return;
92
+ }
93
+
94
+ safeNode.setAttribute(name, attrValue);
95
+ });
96
+
97
+ Array.from(node.childNodes || []).forEach((child) => {
98
+ const safeChild = sanitizeSvgNode(child, allowedTags, allowedAttrs);
99
+ if (safeChild) {
100
+ safeNode.append(safeChild);
101
+ }
102
+ });
103
+
104
+ return safeNode;
105
+ }
106
+
107
+ export function toSafeHtmlString(value, {
108
+ allowedSvgTags = DEFAULT_ALLOWED_SVG_TAGS,
109
+ allowedSvgAttrs = DEFAULT_ALLOWED_SVG_ATTRS
110
+ } = {}) {
111
+ if (value === null || value === undefined) {
112
+ return "";
113
+ }
114
+
115
+ const content = String(value).trim();
116
+ if (!content) {
117
+ return "";
118
+ }
119
+
120
+ if (!content.includes("<")) {
121
+ return escapeHtml(content);
122
+ }
123
+
124
+ const template = document.createElement("template");
125
+ template.innerHTML = content;
126
+
127
+ const sanitizedParts = Array.from(template.content.childNodes)
128
+ .map((node) => sanitizeSvgNode(node, allowedSvgTags, allowedSvgAttrs))
129
+ .filter(Boolean)
130
+ .map((node) => {
131
+ if (node.nodeType === Node.TEXT_NODE) {
132
+ return escapeHtml(node.textContent || "");
133
+ }
134
+ if (node.nodeType === Node.ELEMENT_NODE) {
135
+ return node.outerHTML;
136
+ }
137
+ return "";
138
+ });
139
+
140
+ const sanitized = sanitizedParts.join("");
141
+ return sanitized || escapeHtml(content);
142
+ }
143
+
144
+ class Sanitize {
145
+ static toSafeHtmlString(value, options) {
146
+ return toSafeHtmlString(value, options);
147
+ }
148
+ }
149
+
150
+ export default Sanitize;
@@ -1,140 +1,140 @@
1
- const VIDEO_EXTENSIONS = new Set([
2
- 'mp4',
3
- 'webm',
4
- 'mov',
5
- 'mkv',
6
- 'avi',
7
- 'm4v',
8
- 'ogv'
9
- ]);
10
-
11
- const getFileExtension = (file) => {
12
- const fileName = String(file?.name || '').toLowerCase();
13
- const dot = fileName.lastIndexOf('.');
14
- if (dot < 0 || dot >= fileName.length - 1) {
15
- return '';
16
- }
17
- return fileName.slice(dot + 1);
18
- };
19
-
20
- const waitEvent = (node, eventName) => new Promise((resolve, reject) => {
21
- const onResolve = () => {
22
- cleanup();
23
- resolve(true);
24
- };
25
-
26
- const onReject = () => {
27
- cleanup();
28
- reject(new Error(eventName));
29
- };
30
-
31
- const cleanup = () => {
32
- node.removeEventListener(eventName, onResolve);
33
- node.removeEventListener('error', onReject);
34
- };
35
-
36
- node.addEventListener(eventName, onResolve, { once: true });
37
- node.addEventListener('error', onReject, { once: true });
38
- });
39
-
40
- const seekVideo = (video, time) => new Promise((resolve, reject) => {
41
- const onSeeked = () => {
42
- cleanup();
43
- resolve(true);
44
- };
45
-
46
- const onError = () => {
47
- cleanup();
48
- reject(new Error('seek'));
49
- };
50
-
51
- const cleanup = () => {
52
- video.removeEventListener('seeked', onSeeked);
53
- video.removeEventListener('error', onError);
54
- };
55
-
56
- video.addEventListener('seeked', onSeeked, { once: true });
57
- video.addEventListener('error', onError, { once: true });
58
- video.currentTime = time;
59
- });
60
-
61
- const canvasToBlob = (canvas, type = 'image/jpeg', quality = 0.92) => new Promise((resolve) => {
62
- canvas.toBlob((blob) => resolve(blob || null), type, quality);
63
- });
64
-
65
- const extractVideoMetadata = async (file) => {
66
- if (!(file instanceof File)) {
67
- return null;
68
- }
69
-
70
- const ext = getFileExtension(file);
71
- const isVideoType = String(file.type || '').toLowerCase().startsWith('video/');
72
- if (!isVideoType && !VIDEO_EXTENSIONS.has(ext)) {
73
- return null;
74
- }
75
-
76
- if (!file.size) {
77
- return null;
78
- }
79
-
80
- let objectUrl = '';
81
- try {
82
- objectUrl = URL.createObjectURL(file);
83
-
84
- const video = document.createElement('video');
85
- video.preload = 'metadata';
86
- video.muted = true;
87
- video.playsInline = true;
88
- video.crossOrigin = 'anonymous';
89
- video.src = objectUrl;
90
-
91
- await waitEvent(video, 'loadedmetadata');
92
-
93
- const width = Number(video.videoWidth || 0);
94
- const height = Number(video.videoHeight || 0);
95
- const duration = Number(video.duration || 0);
96
-
97
- if (!width || !height || !Number.isFinite(width) || !Number.isFinite(height)) {
98
- return {
99
- duration: Number.isFinite(duration) ? duration : 0,
100
- width: 0,
101
- height: 0
102
- };
103
- }
104
-
105
- const seekTime = duration > 1 ? Math.min(1, Math.max(0, duration / 2)) : 0;
106
- await seekVideo(video, seekTime);
107
-
108
- const canvas = document.createElement('canvas');
109
- canvas.width = width;
110
- canvas.height = height;
111
- const ctx = canvas.getContext('2d');
112
- if (!ctx) {
113
- return {
114
- duration: Number.isFinite(duration) ? duration : 0,
115
- width,
116
- height
117
- };
118
- }
119
-
120
- ctx.drawImage(video, 0, 0, width, height);
121
- const posterBlob = await canvasToBlob(canvas, 'image/jpeg', 0.92);
122
-
123
- return {
124
- duration: Number.isFinite(duration) ? duration : 0,
125
- width,
126
- height,
127
- posterBlob: posterBlob || null
128
- };
129
- } catch {
130
- return null;
131
- } finally {
132
- if (objectUrl) {
133
- URL.revokeObjectURL(objectUrl);
134
- }
135
- }
136
- };
137
-
138
- export {
139
- extractVideoMetadata
140
- };
1
+ const VIDEO_EXTENSIONS = new Set([
2
+ 'mp4',
3
+ 'webm',
4
+ 'mov',
5
+ 'mkv',
6
+ 'avi',
7
+ 'm4v',
8
+ 'ogv'
9
+ ]);
10
+
11
+ const getFileExtension = (file) => {
12
+ const fileName = String(file?.name || '').toLowerCase();
13
+ const dot = fileName.lastIndexOf('.');
14
+ if (dot < 0 || dot >= fileName.length - 1) {
15
+ return '';
16
+ }
17
+ return fileName.slice(dot + 1);
18
+ };
19
+
20
+ const waitEvent = (node, eventName) => new Promise((resolve, reject) => {
21
+ const onResolve = () => {
22
+ cleanup();
23
+ resolve(true);
24
+ };
25
+
26
+ const onReject = () => {
27
+ cleanup();
28
+ reject(new Error(eventName));
29
+ };
30
+
31
+ const cleanup = () => {
32
+ node.removeEventListener(eventName, onResolve);
33
+ node.removeEventListener('error', onReject);
34
+ };
35
+
36
+ node.addEventListener(eventName, onResolve, { once: true });
37
+ node.addEventListener('error', onReject, { once: true });
38
+ });
39
+
40
+ const seekVideo = (video, time) => new Promise((resolve, reject) => {
41
+ const onSeeked = () => {
42
+ cleanup();
43
+ resolve(true);
44
+ };
45
+
46
+ const onError = () => {
47
+ cleanup();
48
+ reject(new Error('seek'));
49
+ };
50
+
51
+ const cleanup = () => {
52
+ video.removeEventListener('seeked', onSeeked);
53
+ video.removeEventListener('error', onError);
54
+ };
55
+
56
+ video.addEventListener('seeked', onSeeked, { once: true });
57
+ video.addEventListener('error', onError, { once: true });
58
+ video.currentTime = time;
59
+ });
60
+
61
+ const canvasToBlob = (canvas, type = 'image/jpeg', quality = 0.92) => new Promise((resolve) => {
62
+ canvas.toBlob((blob) => resolve(blob || null), type, quality);
63
+ });
64
+
65
+ const extractVideoMetadata = async (file) => {
66
+ if (!(file instanceof File)) {
67
+ return null;
68
+ }
69
+
70
+ const ext = getFileExtension(file);
71
+ const isVideoType = String(file.type || '').toLowerCase().startsWith('video/');
72
+ if (!isVideoType && !VIDEO_EXTENSIONS.has(ext)) {
73
+ return null;
74
+ }
75
+
76
+ if (!file.size) {
77
+ return null;
78
+ }
79
+
80
+ let objectUrl = '';
81
+ try {
82
+ objectUrl = URL.createObjectURL(file);
83
+
84
+ const video = document.createElement('video');
85
+ video.preload = 'metadata';
86
+ video.muted = true;
87
+ video.playsInline = true;
88
+ video.crossOrigin = 'anonymous';
89
+ video.src = objectUrl;
90
+
91
+ await waitEvent(video, 'loadedmetadata');
92
+
93
+ const width = Number(video.videoWidth || 0);
94
+ const height = Number(video.videoHeight || 0);
95
+ const duration = Number(video.duration || 0);
96
+
97
+ if (!width || !height || !Number.isFinite(width) || !Number.isFinite(height)) {
98
+ return {
99
+ duration: Number.isFinite(duration) ? duration : 0,
100
+ width: 0,
101
+ height: 0
102
+ };
103
+ }
104
+
105
+ const seekTime = duration > 1 ? Math.min(1, Math.max(0, duration / 2)) : 0;
106
+ await seekVideo(video, seekTime);
107
+
108
+ const canvas = document.createElement('canvas');
109
+ canvas.width = width;
110
+ canvas.height = height;
111
+ const ctx = canvas.getContext('2d');
112
+ if (!ctx) {
113
+ return {
114
+ duration: Number.isFinite(duration) ? duration : 0,
115
+ width,
116
+ height
117
+ };
118
+ }
119
+
120
+ ctx.drawImage(video, 0, 0, width, height);
121
+ const posterBlob = await canvasToBlob(canvas, 'image/jpeg', 0.92);
122
+
123
+ return {
124
+ duration: Number.isFinite(duration) ? duration : 0,
125
+ width,
126
+ height,
127
+ posterBlob: posterBlob || null
128
+ };
129
+ } catch {
130
+ return null;
131
+ } finally {
132
+ if (objectUrl) {
133
+ URL.revokeObjectURL(objectUrl);
134
+ }
135
+ }
136
+ };
137
+
138
+ export {
139
+ extractVideoMetadata
140
+ };
@@ -1,6 +1,6 @@
1
1
  @use "mixin" as mix;
2
2
  @use "variables" as vars;
3
-
3
+
4
4
  .vg-backdrop {
5
5
  @include mix.mix-vars('backdrop', vars.$backdrop-map);
6
6
  position: fixed;
@@ -11,9 +11,9 @@
11
11
  background: var(--vg-backdrop-bg);
12
12
  transition: var(--vg-backdrop-transition);
13
13
  opacity: 0;
14
- z-index: var(--vg-backdrop-z-index);
15
-
16
- &.fade.show {
14
+ z-index: var(--vg-backdrop-z-index);
15
+
16
+ &.fade.show {
17
17
  opacity: var(--vg-backdrop-opacity);
18
18
  }
19
- }
19
+ }
@@ -1,12 +1,12 @@
1
- @use "theme";
2
-
3
- // Default adapter for the ready-made VGApp stylesheet. Projects that own
4
- // their theme selector can use vgapp/theme and emit the same variables there.
5
- :root,
6
- [data-vg-theme="light"] {
7
- @include theme.theme-variables(theme.$theme-light);
8
- }
9
-
10
- [data-vg-theme="dark"] {
11
- @include theme.theme-variables(theme.$theme-dark);
12
- }
1
+ @use "theme";
2
+
3
+ // Default adapter for the ready-made VGApp stylesheet. Projects that own
4
+ // their theme selector can use vgapp/theme and emit the same variables there.
5
+ :root,
6
+ [data-vg-theme="light"] {
7
+ @include theme.theme-variables(theme.$theme-light);
8
+ }
9
+
10
+ [data-vg-theme="dark"] {
11
+ @include theme.theme-variables(theme.$theme-dark);
12
+ }
@@ -1,7 +1,7 @@
1
- @forward "tokens";
2
-
3
- @mixin theme-variables($theme) {
4
- @each $token, $value in $theme {
5
- --vg-#{$token}: #{$value};
6
- }
7
- }
1
+ @forward "tokens";
2
+
3
+ @mixin theme-variables($theme) {
4
+ @each $token, $value in $theme {
5
+ --vg-#{$token}: #{$value};
6
+ }
7
+ }