@aglyn/plugins-mui 1.0.0-beta.143

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 (177) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +22 -0
  3. package/package.json +47 -0
  4. package/src/index.d.ts +21 -0
  5. package/src/index.js +22 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/components/accordion.d.ts +96 -0
  8. package/src/lib/components/accordion.js +392 -0
  9. package/src/lib/components/accordion.js.map +1 -0
  10. package/src/lib/components/app-bar.d.ts +32 -0
  11. package/src/lib/components/app-bar.js +86 -0
  12. package/src/lib/components/app-bar.js.map +1 -0
  13. package/src/lib/components/blocks.d.ts +46 -0
  14. package/src/lib/components/blocks.js +1007 -0
  15. package/src/lib/components/blocks.js.map +1 -0
  16. package/src/lib/components/box.d.ts +47 -0
  17. package/src/lib/components/box.js +125 -0
  18. package/src/lib/components/box.js.map +1 -0
  19. package/src/lib/components/breadcrumbs.d.ts +44 -0
  20. package/src/lib/components/breadcrumbs.js +172 -0
  21. package/src/lib/components/breadcrumbs.js.map +1 -0
  22. package/src/lib/components/button.d.ts +89 -0
  23. package/src/lib/components/button.js +272 -0
  24. package/src/lib/components/button.js.map +1 -0
  25. package/src/lib/components/card.d.ts +61 -0
  26. package/src/lib/components/card.js +336 -0
  27. package/src/lib/components/card.js.map +1 -0
  28. package/src/lib/components/collection.d.ts +514 -0
  29. package/src/lib/components/collection.js +2999 -0
  30. package/src/lib/components/collection.js.map +1 -0
  31. package/src/lib/components/container.d.ts +42 -0
  32. package/src/lib/components/container.js +169 -0
  33. package/src/lib/components/container.js.map +1 -0
  34. package/src/lib/components/custom-html.d.ts +78 -0
  35. package/src/lib/components/custom-html.js +219 -0
  36. package/src/lib/components/custom-html.js.map +1 -0
  37. package/src/lib/components/data-table.d.ts +57 -0
  38. package/src/lib/components/data-table.js +216 -0
  39. package/src/lib/components/data-table.js.map +1 -0
  40. package/src/lib/components/document-root.d.ts +77 -0
  41. package/src/lib/components/document-root.js +118 -0
  42. package/src/lib/components/document-root.js.map +1 -0
  43. package/src/lib/components/drawer.d.ts +93 -0
  44. package/src/lib/components/drawer.js +566 -0
  45. package/src/lib/components/drawer.js.map +1 -0
  46. package/src/lib/components/function-controls.d.ts +66 -0
  47. package/src/lib/components/function-controls.js +207 -0
  48. package/src/lib/components/function-controls.js.map +1 -0
  49. package/src/lib/components/function-scope.d.ts +110 -0
  50. package/src/lib/components/function-scope.js +478 -0
  51. package/src/lib/components/function-scope.js.map +1 -0
  52. package/src/lib/components/function-widget.d.ts +75 -0
  53. package/src/lib/components/function-widget.js +307 -0
  54. package/src/lib/components/function-widget.js.map +1 -0
  55. package/src/lib/components/grid.d.ts +91 -0
  56. package/src/lib/components/grid.js +322 -0
  57. package/src/lib/components/grid.js.map +1 -0
  58. package/src/lib/components/icon.d.ts +54 -0
  59. package/src/lib/components/icon.js +163 -0
  60. package/src/lib/components/icon.js.map +1 -0
  61. package/src/lib/components/image-list.d.ts +58 -0
  62. package/src/lib/components/image-list.js +336 -0
  63. package/src/lib/components/image-list.js.map +1 -0
  64. package/src/lib/components/image.d.ts +154 -0
  65. package/src/lib/components/image.js +451 -0
  66. package/src/lib/components/image.js.map +1 -0
  67. package/src/lib/components/inline-text.d.ts +74 -0
  68. package/src/lib/components/inline-text.js +407 -0
  69. package/src/lib/components/inline-text.js.map +1 -0
  70. package/src/lib/components/language-switcher.d.ts +35 -0
  71. package/src/lib/components/language-switcher.js +179 -0
  72. package/src/lib/components/language-switcher.js.map +1 -0
  73. package/src/lib/components/layout-slot.d.ts +68 -0
  74. package/src/lib/components/layout-slot.js +192 -0
  75. package/src/lib/components/layout-slot.js.map +1 -0
  76. package/src/lib/components/link-box-accessible-name.d.ts +79 -0
  77. package/src/lib/components/link-box-accessible-name.js +158 -0
  78. package/src/lib/components/link-box-accessible-name.js.map +1 -0
  79. package/src/lib/components/link-box.d.ts +66 -0
  80. package/src/lib/components/link-box.js +212 -0
  81. package/src/lib/components/link-box.js.map +1 -0
  82. package/src/lib/components/list-item-text.d.ts +22 -0
  83. package/src/lib/components/list-item-text.js +86 -0
  84. package/src/lib/components/list-item-text.js.map +1 -0
  85. package/src/lib/components/list-item.d.ts +32 -0
  86. package/src/lib/components/list-item.js +127 -0
  87. package/src/lib/components/list-item.js.map +1 -0
  88. package/src/lib/components/list.d.ts +34 -0
  89. package/src/lib/components/list.js +108 -0
  90. package/src/lib/components/list.js.map +1 -0
  91. package/src/lib/components/markdown.d.ts +129 -0
  92. package/src/lib/components/markdown.js +853 -0
  93. package/src/lib/components/markdown.js.map +1 -0
  94. package/src/lib/components/nav-menu.d.ts +68 -0
  95. package/src/lib/components/nav-menu.js +683 -0
  96. package/src/lib/components/nav-menu.js.map +1 -0
  97. package/src/lib/components/pagination.d.ts +59 -0
  98. package/src/lib/components/pagination.js +243 -0
  99. package/src/lib/components/pagination.js.map +1 -0
  100. package/src/lib/components/paper.d.ts +47 -0
  101. package/src/lib/components/paper.js +202 -0
  102. package/src/lib/components/paper.js.map +1 -0
  103. package/src/lib/components/plugin-frame.d.ts +64 -0
  104. package/src/lib/components/plugin-frame.js +335 -0
  105. package/src/lib/components/plugin-frame.js.map +1 -0
  106. package/src/lib/components/plugin.d.ts +95 -0
  107. package/src/lib/components/plugin.js +304 -0
  108. package/src/lib/components/plugin.js.map +1 -0
  109. package/src/lib/components/product.d.ts +40 -0
  110. package/src/lib/components/product.js +287 -0
  111. package/src/lib/components/product.js.map +1 -0
  112. package/src/lib/components/reusable-instance.d.ts +57 -0
  113. package/src/lib/components/reusable-instance.js +126 -0
  114. package/src/lib/components/reusable-instance.js.map +1 -0
  115. package/src/lib/components/screen-link.d.ts +85 -0
  116. package/src/lib/components/screen-link.js +324 -0
  117. package/src/lib/components/screen-link.js.map +1 -0
  118. package/src/lib/components/search-box.d.ts +41 -0
  119. package/src/lib/components/search-box.js +125 -0
  120. package/src/lib/components/search-box.js.map +1 -0
  121. package/src/lib/components/section.d.ts +56 -0
  122. package/src/lib/components/section.js +132 -0
  123. package/src/lib/components/section.js.map +1 -0
  124. package/src/lib/components/stack.d.ts +55 -0
  125. package/src/lib/components/stack.js +309 -0
  126. package/src/lib/components/stack.js.map +1 -0
  127. package/src/lib/components/tabs.d.ts +288 -0
  128. package/src/lib/components/tabs.js +788 -0
  129. package/src/lib/components/tabs.js.map +1 -0
  130. package/src/lib/components/theme-mode-switcher.d.ts +38 -0
  131. package/src/lib/components/theme-mode-switcher.js +168 -0
  132. package/src/lib/components/theme-mode-switcher.js.map +1 -0
  133. package/src/lib/components/toolbar.d.ts +37 -0
  134. package/src/lib/components/toolbar.js +117 -0
  135. package/src/lib/components/toolbar.js.map +1 -0
  136. package/src/lib/components/typography.d.ts +33 -0
  137. package/src/lib/components/typography.js +433 -0
  138. package/src/lib/components/typography.js.map +1 -0
  139. package/src/lib/components/video-lightbox-controls.d.ts +131 -0
  140. package/src/lib/components/video-lightbox-controls.js +536 -0
  141. package/src/lib/components/video-lightbox-controls.js.map +1 -0
  142. package/src/lib/components/video-lightbox.d.ts +60 -0
  143. package/src/lib/components/video-lightbox.js +247 -0
  144. package/src/lib/components/video-lightbox.js.map +1 -0
  145. package/src/lib/components/video-playback-beacon.d.ts +80 -0
  146. package/src/lib/components/video-playback-beacon.js +128 -0
  147. package/src/lib/components/video-playback-beacon.js.map +1 -0
  148. package/src/lib/components/video-player-frame.d.ts +58 -0
  149. package/src/lib/components/video-player-frame.js +51 -0
  150. package/src/lib/components/video-player-frame.js.map +1 -0
  151. package/src/lib/components/video.d.ts +220 -0
  152. package/src/lib/components/video.js +874 -0
  153. package/src/lib/components/video.js.map +1 -0
  154. package/src/lib/constants/bundle-common.d.ts +17 -0
  155. package/src/lib/constants/bundle-common.js +18 -0
  156. package/src/lib/constants/bundle-common.js.map +1 -0
  157. package/src/lib/constants/field-presets.d.ts +93 -0
  158. package/src/lib/constants/field-presets.js +248 -0
  159. package/src/lib/constants/field-presets.js.map +1 -0
  160. package/src/lib/plugin.d.ts +80 -0
  161. package/src/lib/plugin.js +599 -0
  162. package/src/lib/plugin.js.map +1 -0
  163. package/src/lib/utils/author-html-label.d.ts +73 -0
  164. package/src/lib/utils/author-html-label.js +89 -0
  165. package/src/lib/utils/author-html-label.js.map +1 -0
  166. package/src/lib/utils/drop-cleared-props.d.ts +18 -0
  167. package/src/lib/utils/drop-cleared-props.js +74 -0
  168. package/src/lib/utils/drop-cleared-props.js.map +1 -0
  169. package/src/lib/utils/element-picker.d.ts +99 -0
  170. package/src/lib/utils/element-picker.js +140 -0
  171. package/src/lib/utils/element-picker.js.map +1 -0
  172. package/src/lib/utils/generate-preset-id.d.ts +19 -0
  173. package/src/lib/utils/generate-preset-id.js +25 -0
  174. package/src/lib/utils/generate-preset-id.js.map +1 -0
  175. package/src/lib/utils/link-target-props.d.ts +53 -0
  176. package/src/lib/utils/link-target-props.js +53 -0
  177. package/src/lib/utils/link-target-props.js.map +1 -0
@@ -0,0 +1,874 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
3
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
+ /**
5
+ * @license
6
+ * Copyright 2026 Aglyn LLC
7
+ *
8
+ * Licensed under the Apache License, Version 2.0 (the "License");
9
+ * you may not use this file except in compliance with the License.
10
+ * You may obtain a copy of the License at
11
+ *
12
+ * http://www.apache.org/licenses/LICENSE-2.0
13
+ *
14
+ * Unless required by applicable law or agreed to in writing, software
15
+ * distributed under the License is distributed on an "AS IS" BASIS,
16
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ * See the License for the specific language governing permissions and
18
+ * limitations under the License.
19
+ */ import * as Aglyn from "@aglyn/aglyn";
20
+ import { readStoredVisitorConsent, VISITOR_CONSENT_CHANGED_EVENT } from "@aglyn/aglyn/app-utils/visitor-consent";
21
+ import { wistiaMediaId, wistiaPlayerSrc } from "@aglyn/aglyn/app-utils/wistia-embed";
22
+ import { mdiPlay, mdiVideo } from "@aglyn/shared-data-mdi";
23
+ import { MdiIcon } from "@aglyn/shared-ui-jsx";
24
+ import Box from "@mui/material/Box";
25
+ import useEventCallback from "@mui/utils/useEventCallback";
26
+ import useForkRef from "@mui/utils/useForkRef";
27
+ import { Suspense, forwardRef, lazy, useCallback, useEffect, useState } from "react";
28
+ import { BUNDLE_ID } from "../constants/bundle-common.js";
29
+ import { generatePresetId } from "../utils/generate-preset-id.js";
30
+ import { useVideoPlaybackBeacon } from "./video-playback-beacon.js";
31
+ import { VideoPlayerFrame } from "./video-player-frame.js";
32
+ /**
33
+ * The lightbox, behind a lazy boundary rather than a plain import (AGL-2744).
34
+ *
35
+ * `video-lightbox.tsx` is the only module in the tenant's component graph that
36
+ * names `@mui/material/Dialog`, and `plugin.ts` imports every element in this
37
+ * bundle eagerly — so naming it here would put the whole MUI Dialog stack on
38
+ * every published page of every customer site, which is precisely what
39
+ * AGL-1290 took OFF those pages by withholding `dialog-confirm` and
40
+ * `navigation-drawer` from the shared JSX barrel.
41
+ *
42
+ * Behind `lazy()` it is a chunk nothing on a first paint requests: not on a
43
+ * page without a video, not on a page with one, only for a visitor who has
44
+ * asked to watch. Same shape as `product-detail.tsx`'s Payment Element, for
45
+ * the same reason.
46
+ */ const VideoLightbox = /*#__PURE__*/ lazy(()=>import("./video-lightbox.js").then((module)=>({
47
+ default: module.VideoLightbox
48
+ })));
49
+ // Component ids are persisted in screen documents; never rename.
50
+ export const ID = 'video';
51
+ /**
52
+ * The width the poster is requested at.
53
+ *
54
+ * `<video poster>` takes ONE url and no `srcset`, so unlike `image.tsx` there
55
+ * is no candidate list to hand the browser and no `sizes` to resolve against —
56
+ * a single width has to serve every viewport. Asking for it is what turns a
57
+ * 335 KB PNG poster into the 4 KB WebP the DAM already generated.
58
+ *
59
+ * Read from `@aglyn/aglyn` rather than restated: the page's `thumbnailUrl`
60
+ * asks for the same width, and a crawler that fetches a different image from
61
+ * the one a visitor sees is describing a different picture.
62
+ */ const POSTER_REQUEST_WIDTH = Aglyn.MEDIA_CDN_POSTER_WIDTH;
63
+ /**
64
+ * What `preload` an unset field resolves to, given the rest of the node.
65
+ *
66
+ * Exported for the spec, because the whole point of this element is what it
67
+ * does NOT download and a rule that is only asserted through rendered markup
68
+ * is a rule three refactors from being an accident.
69
+ *
70
+ * * **Autoplay wins first.** A video that starts on its own needs its bytes
71
+ * by definition; `none` there is a hint every browser overrides anyway, and
72
+ * stating it would only mislead the next reader.
73
+ * * **A poster buys `none`.** This is the saving the element exists for — the
74
+ * poster carries the first paint at image cost, and not one byte of video
75
+ * is fetched until a visitor presses play.
76
+ * * **No poster keeps `metadata`.** With neither a poster nor metadata a
77
+ * `<video>` paints a black rectangle, so deferring an unpostered video
78
+ * trades a real transfer for a real regression. `metadata` is also what
79
+ * every document authored before this rendered, which is what makes the
80
+ * change safe for sites already published.
81
+ */ export function resolveVideoPreload(options) {
82
+ const { preload, poster, autoPlay } = options != null ? options : {};
83
+ if (preload === 'none' || preload === 'metadata' || preload === 'auto') return preload;
84
+ if (autoPlay) return 'auto';
85
+ return poster ? 'none' : 'metadata';
86
+ }
87
+ /**
88
+ * Video element (AGL-162, AGL-2741): plays a media-library upload, any URL or
89
+ * a Wistia video (AGL-2826), poster first.
90
+ *
91
+ * The default is now to download NOTHING until a visitor asks. A poster is an
92
+ * image — a few tens of kilobytes through the DAM's WebP variants, edge-cached
93
+ * — while the video behind it is not edge-cacheable at all and is measured in
94
+ * megabytes. `preload` decides which of those a page pays for on first paint,
95
+ * and {@link resolveVideoPreload} explains what an unset field resolves to.
96
+ *
97
+ * An empty src shows a labeled placeholder so the element stays selectable in
98
+ * the editor.
99
+ */ const Video = /*#__PURE__*/ forwardRef((props, ref)=>{
100
+ const { src: storedSrc, poster: storedPoster, posterFromSource, title, // Structured-data-only fields. Every one of them MUST be destructured:
101
+ // what is left in `rest` is spread onto the `<video>`, where a
102
+ // `description` or an `uploadDate` becomes an invalid DOM attribute in
103
+ // the published HTML.
104
+ description: _description, uploadDate: _uploadDate, durationSeconds: _durationSeconds, lightbox, loadPlayer, captionsSrc, captionsLabel, captionsLang, autoPlay, loop, muted, controls, playsInline, preload, width, height, intrinsicWidth, intrinsicHeight, radius, children: _children, // Pulled out of the spread: the literals below are composed AFTER
105
+ // `{...rest}`, so leaving it there REPLACES every style the author set
106
+ // from the Styles panel (AGL-1240, the same trap image.tsx documents).
107
+ sx: nodeSxProp } = props, rest = _object_without_properties_loose(props, [
108
+ "src",
109
+ "poster",
110
+ "posterFromSource",
111
+ "title",
112
+ "description",
113
+ "uploadDate",
114
+ "durationSeconds",
115
+ "lightbox",
116
+ "loadPlayer",
117
+ "captionsSrc",
118
+ "captionsLabel",
119
+ "captionsLang",
120
+ "autoPlay",
121
+ "loop",
122
+ "muted",
123
+ "controls",
124
+ "playsInline",
125
+ "preload",
126
+ "width",
127
+ "height",
128
+ "intrinsicWidth",
129
+ "intrinsicHeight",
130
+ "radius",
131
+ "children",
132
+ "sx"
133
+ ]);
134
+ const nodeSx = Array.isArray(nodeSxProp) ? nodeSxProp : nodeSxProp ? [
135
+ nodeSxProp
136
+ ] : [];
137
+ // Both fields are media-picker targets, so both can hold a media
138
+ // reference (AGL-1215) as well as any of the URL forms that predate it.
139
+ // Resolving here is not optional: the picker hands back one string for
140
+ // whichever attribute asked for it, so every field it can write to has to
141
+ // understand the same value.
142
+ const { hostId } = Aglyn.useSite();
143
+ /**
144
+ * The delivery copy, not the master (AGL-2753).
145
+ *
146
+ * `videoDeliverySrc` is `resolveMediaSrc` plus `?r=auto`, which asks the CDN
147
+ * for the best encoding it holds instead of naming one. The element cannot
148
+ * name one: renditions are produced out of band by
149
+ * `tools/scripts/generate-video-renditions.mjs`, minutes or days after the
150
+ * upload, so at pick time an asset usually has none and a copied list would
151
+ * be empty for exactly the videos it exists to serve. Asking rather than
152
+ * naming is what lets an encoding made tomorrow reach a video placed today,
153
+ * with no re-pick and nothing rewritten in a published document.
154
+ *
155
+ * Nothing here is conditional on the asset having renditions, because the
156
+ * URL degrades to the master by construction — which is the file this
157
+ * served before, so a page cannot get worse by adopting it.
158
+ */ const src = Aglyn.videoDeliverySrc(storedSrc, {
159
+ hostId
160
+ });
161
+ // Two candidates, one rule, shared with the page's `thumbnailUrl`: the
162
+ // author's own poster wins, and the DAM's generated frame is used only when
163
+ // the node records that one exists (AGL-2749).
164
+ const posterFor = (width)=>Aglyn.videoPosterSrc({
165
+ hostId,
166
+ poster: storedPoster,
167
+ src: storedSrc,
168
+ generated: posterFromSource,
169
+ width
170
+ });
171
+ /** Full size — what an `<img>` wants beside a `srcSet` of widths. */ const posterBase = posterFor();
172
+ /** One width — what `<video poster>` and a `thumbnailUrl` have to take. */ const poster = posterFor(POSTER_REQUEST_WIDTH);
173
+ const captions = Aglyn.resolveMediaSrc(captionsSrc, {
174
+ hostId
175
+ });
176
+ /**
177
+ * The lightbox's two pieces of state, and they are deliberately two.
178
+ *
179
+ * `open` is what the dialog reads. `armed` is whether the chunk has been
180
+ * ASKED for, which happens on the first hover or focus rather than on the
181
+ * click — so by the time a visitor's pointer has travelled from the poster
182
+ * to the play badge, the module is usually already there and the dialog
183
+ * opens on the same frame as the click. A visitor who never goes near the
184
+ * poster never arms it and never fetches it, which is the whole bargain.
185
+ *
186
+ * `armed` never goes back to false. Re-mounting `lazy()` after a close
187
+ * would not re-download anything (the module is resolved), and leaving it
188
+ * mounted is what lets a second open be instant.
189
+ */ const [open, setOpen] = useState(false);
190
+ const [armed, setArmed] = useState(false);
191
+ const arm = useCallback(()=>setArmed(true), []);
192
+ const closeLightbox = useCallback(()=>setOpen(false), []);
193
+ const openLightbox = useCallback(()=>{
194
+ setArmed(true);
195
+ setOpen(true);
196
+ }, []);
197
+ /**
198
+ * The besigner canvas is inert (AGL-830): opening a focus-trapping dialog
199
+ * inside the editor would steal the author's keyboard mid-edit, and the
200
+ * click on a poster there means "select this node", not "watch this".
201
+ * The trigger still RENDERS as a button, because what an author sees on the
202
+ * canvas has to be what a visitor gets.
203
+ */ const { editorInert, suppressNavigation } = Aglyn.useScreenLink(undefined);
204
+ /**
205
+ * What a play is counted against (AGL-2781): the site, the asset the STORED
206
+ * value names, and whether this surface is an editor. Handed to the
207
+ * lightbox as data rather than as handlers, because a play there belongs to
208
+ * one open of the dialog and only the dialog knows when that begins.
209
+ */ const playback = {
210
+ hostId,
211
+ src: storedSrc,
212
+ suppressed: suppressNavigation
213
+ };
214
+ const playbackHandlers = useVideoPlaybackBeacon(playback);
215
+ /**
216
+ * A Wistia video (AGL-2826), and the player address a press produced.
217
+ *
218
+ * Unless the page loads the player (see `pageLoadSrc` below), nothing of
219
+ * Wistia's renders before that press. The poster is a button, as it is for
220
+ * the lightbox, so a visitor who only looks at the page makes no request to
221
+ * Wistia and is handed no storage by it. `playerSrc` stays empty until the
222
+ * press, and setting it is what brings the frame in.
223
+ */ const wistiaId = wistiaMediaId(storedSrc);
224
+ const [playerSrc, setPlayerSrc] = useState();
225
+ /**
226
+ * The press. Consent is read here and not during render: the record lives
227
+ * in this browser's storage, which a cached page cannot vary on, and a
228
+ * visitor may answer the banner between the first paint and the click.
229
+ *
230
+ * Wistia records the viewing only when this visitor's analytics consent is
231
+ * on record. A visitor without that record — still deciding, refused,
232
+ * opted out by GPC, or visiting a site that runs no consent banner — gets
233
+ * the player with `doNotTrack`.
234
+ */ const playWistia = useCallback(()=>{
235
+ var _readStoredVisitorConsent;
236
+ setPlayerSrc(wistiaPlayerSrc(storedSrc, {
237
+ doNotTrack: !((_readStoredVisitorConsent = readStoredVisitorConsent(hostId)) == null ? void 0 : _readStoredVisitorConsent.analytics),
238
+ muted: Boolean(muted),
239
+ loop: Boolean(loop)
240
+ }));
241
+ }, [
242
+ storedSrc,
243
+ hostId,
244
+ muted,
245
+ loop
246
+ ]);
247
+ /**
248
+ * The player address "Load the player with the page" produced (AGL-2962):
249
+ * Wistia's player without autoplay, in place of the poster before anyone has
250
+ * pressed anything, so a search engine rendering the page finds the player
251
+ * in it.
252
+ *
253
+ * Only for a visitor whose stored consent grants analytics, which is why the
254
+ * address never carries `doNotTrack`. The unpressed frame is not free of
255
+ * storage: measured on 2026-09-14 in headless Chrome, it set no cookie but
256
+ * wrote two keys to its own `localStorage`, a random resume key and a record
257
+ * of its load times, with `doNotTrack` on as well as off. A visitor in a
258
+ * prior-consent region has to say yes before that. Everyone else keeps the
259
+ * poster, and a press on it loads the player as it does with the switch
260
+ * off, `doNotTrack` included.
261
+ *
262
+ * Set from an effect and never during render, for the reason a press reads
263
+ * consent at the click: the record is in this browser's storage, which the
264
+ * server HTML and the first paint cannot vary on, so both are the poster for
265
+ * every visitor. The effect reads the record on mount and again on every
266
+ * consent change, because the decision can land after the element mounts:
267
+ * the implied default is recorded when the region lookup answers, and a
268
+ * banner is answered whenever the visitor gets to it. A withdrawal before
269
+ * any press on the element puts the poster back, even over a film started
270
+ * from the play button inside Wistia's frame, which the element cannot see.
271
+ *
272
+ * Never on the besigner canvas, where the poster button is inert too, and
273
+ * never for a Wistia video without a poster, which has no first paint to
274
+ * give. A press ends it: from then on the pressed address is the one in the
275
+ * frame, and a later change of consent moves nothing, as it moves nothing
276
+ * for a player a press loaded with the switch off.
277
+ */ const [pageLoadSrc, setPageLoadSrc] = useState();
278
+ useEffect(()=>{
279
+ if (!loadPlayer || !wistiaId || !poster || editorInert || playerSrc) {
280
+ return undefined;
281
+ }
282
+ const sync = ()=>{
283
+ var _readStoredVisitorConsent;
284
+ return setPageLoadSrc(((_readStoredVisitorConsent = readStoredVisitorConsent(hostId)) == null ? void 0 : _readStoredVisitorConsent.analytics) ? wistiaPlayerSrc(storedSrc, {
285
+ autoPlay: false,
286
+ muted: Boolean(muted),
287
+ loop: Boolean(loop)
288
+ }) : undefined);
289
+ };
290
+ sync();
291
+ window.addEventListener(VISITOR_CONSENT_CHANGED_EVENT, sync);
292
+ return ()=>window.removeEventListener(VISITOR_CONSENT_CHANGED_EVENT, sync);
293
+ }, [
294
+ loadPlayer,
295
+ wistiaId,
296
+ poster,
297
+ editorInert,
298
+ playerSrc,
299
+ hostId,
300
+ storedSrc,
301
+ muted,
302
+ loop
303
+ ]);
304
+ /** Read through the switch, so switching it off removes a loaded player. */ const loadedSrc = loadPlayer ? pageLoadSrc : undefined;
305
+ /**
306
+ * The address of the Wistia player standing in place of the poster, if one
307
+ * is: a pressed player while the lightbox is off, and a player the page
308
+ * loaded whatever the lightbox is set to. The pressed address outranks the
309
+ * loaded one, because it is the one that plays.
310
+ */ const inPlaceSrc = wistiaId && (!lightbox || loadedSrc) ? playerSrc != null ? playerSrc : loadedSrc : undefined;
311
+ /**
312
+ * What pressing the poster does. The poster's own button and a press sent
313
+ * from outside the element both call this one function, so the two cannot
314
+ * come to disagree about what a press is.
315
+ *
316
+ * A player the page loaded is already in place, so a press plays it there
317
+ * and never opens the lightbox.
318
+ */ const press = ()=>{
319
+ if (wistiaId) playWistia();
320
+ if (lightbox && !loadedSrc) openLightbox();
321
+ };
322
+ /**
323
+ * A press sent from outside the element (AGL-2867) — a "Play a video"
324
+ * interaction on a button elsewhere on the page — answered as the markup
325
+ * rendered below would answer a press on it, branch for branch:
326
+ *
327
+ * - nothing that can play (no source, or a Wistia video with no poster):
328
+ * nothing happens;
329
+ * - a Wistia player a press put in place of its poster: it is playing
330
+ * already;
331
+ * - a Wistia player the page loaded and nothing has pressed: `press()`,
332
+ * whose autoplaying address replaces the loaded one. That is what the play
333
+ * button inside the frame does, and the element cannot reach that button
334
+ * across Wistia's origin, so a film already started from it starts over;
335
+ * - a poster that is a button: that button's own press, which reads consent
336
+ * at that moment and is the only thing that ever loads the lightbox chunk
337
+ * or a player the page did not load;
338
+ * - the inline player: `play()`, which is what a press on it does.
339
+ *
340
+ * Inert on the besigner canvas, as the poster button is, for the same
341
+ * reason: nothing may load a player or open a dialog inside the editor.
342
+ *
343
+ * `useEventCallback`, not React's `useEffectEvent`: in the React this repo
344
+ * runs, an effect event declared in a `forwardRef` component is never
345
+ * handed the implementation from later renders, so a listener added once
346
+ * would go on answering with the props of the first one.
347
+ */ const answerVideoCommand = useEventCallback((element)=>{
348
+ if (editorInert || !src) return;
349
+ if (wistiaId && !poster) return;
350
+ if (inPlaceSrc && playerSrc) return;
351
+ if ((lightbox || wistiaId) && poster) return press();
352
+ if (element instanceof HTMLVideoElement) {
353
+ // A play the browser refuses (a trigger that was not a press, on an
354
+ // unmuted film) is the browser's decision to make, not an error.
355
+ void Promise.resolve(element.play()).catch(()=>undefined);
356
+ }
357
+ });
358
+ /**
359
+ * The element's own DOM root, which receives the command. State rather than
360
+ * a ref object, because the root is a different element in different
361
+ * branches — the inline `<video>`, or the box around a poster — and the
362
+ * listener has to follow it.
363
+ */ const [root, setRoot] = useState(null);
364
+ const rootRef = useForkRef(ref, setRoot);
365
+ useEffect(()=>{
366
+ if (!root) return undefined;
367
+ return Aglyn.subscribeVideoCommands(root, ()=>answerVideoCommand(root));
368
+ }, [
369
+ root,
370
+ answerVideoCommand
371
+ ]);
372
+ /**
373
+ * The labeled box the element shows when it has nothing it can play. The
374
+ * label is for the author, so only editing surfaces draw it; a published
375
+ * page renders the bare element.
376
+ */ const placeholder = (label)=>suppressNavigation ? /*#__PURE__*/ _jsx(Box, _extends({
377
+ ref: rootRef
378
+ }, rest, {
379
+ sx: [
380
+ {
381
+ width: width || '100%',
382
+ height: height || 180,
383
+ display: 'flex',
384
+ alignItems: 'center',
385
+ justifyContent: 'center',
386
+ border: '1px dashed',
387
+ borderColor: 'divider',
388
+ borderRadius: radius != null ? `${radius}px` : undefined,
389
+ color: 'text.secondary',
390
+ fontSize: 12,
391
+ fontFamily: 'system-ui, sans-serif'
392
+ },
393
+ ...nodeSx
394
+ ],
395
+ children: label
396
+ })) : /*#__PURE__*/ _jsx(Box, _extends({
397
+ ref: rootRef
398
+ }, rest, {
399
+ sx: nodeSx
400
+ }));
401
+ if (!src) return placeholder('Video — set a source URL');
402
+ /**
403
+ * The reserved box, as a ratio rather than a size.
404
+ *
405
+ * Both-or-neither and finite-and-positive for the reasons `image.tsx` gives
406
+ * at its own pair: a lone dimension describes no shape, and a capture may
407
+ * legitimately be `0` or missing. Suppressed when the author pinned a
408
+ * `height`, because then the box is already definite and a ratio would only
409
+ * fight the value they typed.
410
+ */ const usable = (value)=>typeof value === 'number' && Number.isFinite(value) && value > 0;
411
+ const aspectRatio = !height && usable(intrinsicWidth) && usable(intrinsicHeight) ? `${intrinsicWidth} / ${intrinsicHeight}` : undefined;
412
+ const track = captions ? // `default` so the track is selected without the visitor hunting for it
413
+ // in the player's menu. A captions file an author took the trouble to
414
+ // attach is one they meant people to see, and the browser still offers
415
+ // the switch that turns it off.
416
+ /*#__PURE__*/ _jsx("track", {
417
+ kind: "captions",
418
+ src: captions,
419
+ srcLang: captionsLang || undefined,
420
+ label: captionsLabel || undefined,
421
+ default: true
422
+ }) : null;
423
+ /**
424
+ * A Wistia video with no poster has nothing to press, and the alternative —
425
+ * loading the player up front for every visitor — is the request this
426
+ * element exists to withhold. So it says what it needs instead, with "Load
427
+ * the player with the page" on as well: the poster is that switch's server
428
+ * HTML and first paint, and what every visitor without analytics consent
429
+ * goes on seeing.
430
+ */ if (wistiaId && !poster) {
431
+ return placeholder('Video — add a poster image to play this Wistia video');
432
+ }
433
+ /**
434
+ * The Wistia player in place of the poster: after a press when the lightbox
435
+ * is off, or loaded with the page (AGL-2962). The wrapper is the same element
436
+ * the poster sat in, so the node's styles and ref stay where they were and
437
+ * only its contents change.
438
+ */ if (inPlaceSrc) {
439
+ return /*#__PURE__*/ _jsx(Box, _extends({
440
+ ref: rootRef
441
+ }, rest, {
442
+ sx: [
443
+ {
444
+ display: 'block',
445
+ position: 'relative',
446
+ width: width || '100%',
447
+ height: height || 'auto'
448
+ },
449
+ ...nodeSx
450
+ ],
451
+ children: /*#__PURE__*/ _jsx(VideoPlayerFrame, {
452
+ src: inPlaceSrc,
453
+ title: title,
454
+ aspectRatio: aspectRatio,
455
+ height: height,
456
+ radius: radius,
457
+ // Focus follows a press into the player. A player the page loaded
458
+ // takes none: nobody asked for it, and focusing a frame as the page
459
+ // loads would scroll the page to it and take the keyboard with it.
460
+ focusOnMount: Boolean(playerSrc)
461
+ }, playerSrc ? 'pressed' : 'loaded')
462
+ }));
463
+ }
464
+ /**
465
+ * Poster-first, dialog on click (AGL-2744).
466
+ *
467
+ * Requires a poster, and falls back to the inline player without one rather
468
+ * than rendering a button with no face. That is not a limitation to work
469
+ * around — a lightbox trigger with nothing to show is a blank rectangle
470
+ * that claims to be a film.
471
+ *
472
+ * A Wistia video takes this branch until a player stands in place of its
473
+ * poster (AGL-2826), which includes the server HTML and first paint of one
474
+ * that loads its player with the page. With the lightbox off its press swaps
475
+ * the player in above, and the lazy dialog is never armed, because nothing is
476
+ * going to open it.
477
+ */ if ((lightbox || wistiaId) && poster) {
478
+ const playsInPlace = Boolean(wistiaId) && !lightbox;
479
+ return /*#__PURE__*/ _jsxs(Box, _extends({
480
+ ref: rootRef
481
+ }, rest, {
482
+ sx: [
483
+ {
484
+ display: 'block',
485
+ position: 'relative',
486
+ width: width || '100%',
487
+ height: height || 'auto'
488
+ },
489
+ ...nodeSx
490
+ ],
491
+ children: [
492
+ /*#__PURE__*/ _jsxs(Box, {
493
+ component: "button",
494
+ type: "button",
495
+ // Inert on the canvas: a focus-trapping dialog opened inside the
496
+ // editor would take the author's keyboard away mid-edit, and the
497
+ // click there means "select this node". Same shape as the Drawer's
498
+ // `editorInert ? undefined : handler`. It is also why a hosted
499
+ // player never loads into the canvas.
500
+ onClick: editorInert ? undefined : press,
501
+ // Arming on hover and on focus, not on click, is what makes the
502
+ // dialog open on the same frame as the press — and it costs a
503
+ // visitor who never approaches the poster nothing at all.
504
+ onPointerEnter: editorInert || playsInPlace ? undefined : arm,
505
+ onFocus: editorInert || playsInPlace ? undefined : arm,
506
+ // The button IS the play control, so it says so. Without a title it
507
+ // still announces its purpose rather than reading as an image.
508
+ "aria-label": title ? `Play video: ${title}` : 'Play video',
509
+ sx: {
510
+ // A button reset: the poster is the control, so none of the
511
+ // browser's own chrome should show through it.
512
+ appearance: 'none',
513
+ border: 0,
514
+ p: 0,
515
+ m: 0,
516
+ display: 'block',
517
+ width: '100%',
518
+ font: 'inherit',
519
+ color: 'inherit',
520
+ background: 'none',
521
+ cursor: editorInert ? 'default' : 'pointer',
522
+ position: 'relative',
523
+ overflow: 'hidden',
524
+ borderRadius: radius != null ? `${radius}px` : undefined,
525
+ // The focus ring is the theme's, not a literal: a keyboard
526
+ // visitor has to be able to see where they are on a poster of
527
+ // unknown brightness.
528
+ '&:focus-visible': {
529
+ outline: '2px solid',
530
+ outlineColor: 'primary.dark',
531
+ outlineOffset: 2
532
+ }
533
+ },
534
+ children: [
535
+ /*#__PURE__*/ _jsx(Box, {
536
+ component: "img",
537
+ src: posterBase,
538
+ // The poster is an `<img>` here rather than a `<video poster>`,
539
+ // which is the one advantage this mode has over the inline one:
540
+ // an `<img>` takes the DAM's whole candidate list, so a phone
541
+ // downloads `?w=320` where the inline player has to be handed a
542
+ // single width for every viewport.
543
+ srcSet: Aglyn.isMediaCdnUrl(posterBase) ? Aglyn.MEDIA_CDN_VARIANT_WIDTHS.map((variant)=>{
544
+ // Built through the same rule as the src, not by pasting
545
+ // `?w=` onto it: a generated poster's url already carries
546
+ // `?poster=1`, so a hand-appended query would produce
547
+ // `?poster=1?w=320` and 404 every candidate.
548
+ const candidate = posterFor(variant);
549
+ return candidate ? `${candidate} ${variant}w` : '';
550
+ }).filter(Boolean).join(', ') : undefined,
551
+ sizes: Aglyn.isMediaCdnUrl(posterBase) ? '100vw' : undefined,
552
+ // Empty by design. The button's `aria-label` already names the
553
+ // film and its purpose; alt text here would make a screen reader
554
+ // read the same thing twice, once as a control and once as a
555
+ // picture.
556
+ alt: "",
557
+ // No `loading` hint, deliberately — see `deferred-images.spec`,
558
+ // where this element is exempt. A poster is the entire visual of
559
+ // a media element an author placed on purpose, and a browser
560
+ // fetches `<video poster>` eagerly too, so deferring it would be
561
+ // a regression against what this element rendered yesterday
562
+ // rather than a saving.
563
+ decoding: "async",
564
+ sx: {
565
+ display: 'block',
566
+ width: '100%',
567
+ height: height || 'auto',
568
+ objectFit: 'cover',
569
+ aspectRatio
570
+ }
571
+ }),
572
+ /*#__PURE__*/ _jsx(Box, {
573
+ "aria-hidden": true,
574
+ sx: {
575
+ position: 'absolute',
576
+ top: '50%',
577
+ left: '50%',
578
+ transform: 'translate(-50%, -50%)',
579
+ display: 'flex',
580
+ alignItems: 'center',
581
+ justifyContent: 'center',
582
+ // A poster is a frame of unknown brightness, and this control
583
+ // has to separate from all of them. A solid disc does; the
584
+ // translucent scrim it replaced sank into any dark frame.
585
+ width: {
586
+ xs: 64,
587
+ sm: 80,
588
+ md: 96
589
+ },
590
+ height: {
591
+ xs: 64,
592
+ sm: 80,
593
+ md: 96
594
+ },
595
+ borderRadius: '50%',
596
+ // The site's accent, not a literal. A play control is the one
597
+ // mark on a poster that should read as the brand's, so it takes
598
+ // the theme's primary the way every other accented glyph does.
599
+ color: 'primary.main',
600
+ bgcolor: 'common.white',
601
+ boxShadow: 6,
602
+ // Gated in CSS rather than behind a JS branch, so toggling the
603
+ // OS setting after load resolves in BOTH directions — the same
604
+ // reason `element-animation-assets` puts every rule it has
605
+ // inside this query.
606
+ '@media (prefers-reduced-motion: no-preference)': {
607
+ transition: 'transform 150ms, color 150ms',
608
+ 'button:hover &, button:focus-visible &': {
609
+ transform: 'translate(-50%, -50%) scale(1.08)',
610
+ color: 'primary.dark'
611
+ }
612
+ }
613
+ },
614
+ children: /*#__PURE__*/ _jsx(MdiIcon, {
615
+ path: mdiPlay.path,
616
+ sx: {
617
+ fontSize: {
618
+ xs: 32,
619
+ sm: 40,
620
+ md: 48
621
+ },
622
+ ml: '4px'
623
+ }
624
+ })
625
+ })
626
+ ]
627
+ }),
628
+ armed ? // `fallback={null}`: the poster is still on screen underneath, so
629
+ // there is nothing to cover and a spinner would only flash. The
630
+ // chunk is usually resolved before the click anyway.
631
+ /*#__PURE__*/ _jsx(Suspense, {
632
+ fallback: null,
633
+ children: /*#__PURE__*/ _jsx(VideoLightbox, {
634
+ open: open,
635
+ onClose: closeLightbox,
636
+ title: title,
637
+ src: src,
638
+ poster: poster,
639
+ aspectRatio: aspectRatio,
640
+ loop: loop,
641
+ muted: muted,
642
+ captions: track,
643
+ playback: playback,
644
+ embedSrc: wistiaId ? playerSrc : undefined
645
+ })
646
+ }) : null
647
+ ]
648
+ }));
649
+ }
650
+ return /*#__PURE__*/ _jsx(Box, _extends({
651
+ ref: rootRef,
652
+ component: "video",
653
+ src: src,
654
+ poster: poster || undefined,
655
+ title: title || undefined,
656
+ autoPlay: Boolean(autoPlay),
657
+ loop: Boolean(loop),
658
+ // Browsers block unmuted autoplay.
659
+ muted: Boolean(muted) || Boolean(autoPlay),
660
+ controls: controls !== false,
661
+ playsInline: playsInline !== false,
662
+ preload: resolveVideoPreload({
663
+ preload,
664
+ poster,
665
+ autoPlay
666
+ })
667
+ }, rest, {
668
+ // After the spread, so a stray prop from the tree cannot unwire the
669
+ // counters.
670
+ onPlay: playbackHandlers.onPlay,
671
+ onTimeUpdate: playbackHandlers.onTimeUpdate,
672
+ onEnded: playbackHandlers.onEnded,
673
+ sx: [
674
+ {
675
+ display: 'block',
676
+ width: width || '100%',
677
+ height: height || 'auto',
678
+ aspectRatio,
679
+ borderRadius: radius != null ? `${radius}px` : undefined
680
+ },
681
+ ...nodeSx
682
+ ],
683
+ children: track
684
+ }));
685
+ });
686
+ Video.displayName = 'Video';
687
+ /**
688
+ * The caption fields say nothing until there is a captions file to label.
689
+ *
690
+ * `isNotEmpty` rather than an inverted `is: ''` — the two are not the same
691
+ * question for a field nobody has touched. An unset attribute is `undefined`,
692
+ * which is not equal to `''`, so `{ is: '', notMatch: true }` would read as
693
+ * "not empty" and show both controls on every fresh Video element.
694
+ */ const HAS_CAPTIONS = {
695
+ when: 'captionsSrc',
696
+ isNotEmpty: true
697
+ };
698
+ export const schema = {
699
+ $id: ID,
700
+ pluginId: BUNDLE_ID,
701
+ displayName: 'Video',
702
+ description: 'Plays a video file from your media library or any URL.',
703
+ category: Aglyn.ComponentCategory.MEDIA,
704
+ icon: {
705
+ path: mdiVideo.path,
706
+ sx: {
707
+ color: '#7b1fa2'
708
+ }
709
+ },
710
+ flags: {
711
+ selfClosing: Aglyn.FEATURE_FLAG.ENABLED
712
+ },
713
+ attributes: [
714
+ {
715
+ name: 'src',
716
+ description: 'Pick the film from your media library with "Browse media", or ' + 'paste the URL of a video hosted somewhere else. A Wistia media ' + "link plays in Wistia's player, which loads when a visitor presses " + 'play. Load the player with the page can load it sooner.',
717
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
718
+ label: 'Video source'
719
+ },
720
+ {
721
+ name: 'poster',
722
+ description: 'The still shown before anyone presses play. Videos from your media ' + 'library already have one; pick a different image here to override ' + 'it. A poster is what keeps the video off the wire until it is asked for. ' + 'A Wistia video needs one: it is the button a visitor presses.',
723
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
724
+ label: 'Poster image'
725
+ },
726
+ {
727
+ name: 'title',
728
+ description: 'Names the video for screen readers, for the tooltip, and for ' + 'search engines. Write it as a headline, not a file name.',
729
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
730
+ label: 'Video title'
731
+ },
732
+ {
733
+ name: 'description',
734
+ description: 'A sentence or two about what the video shows, different for each ' + 'video. Never displayed on the page. Search engines read it, and ' + 'can still list the video in results when it is blank.',
735
+ component: Aglyn.FieldComponentType.TEXTAREA,
736
+ label: 'Video description'
737
+ },
738
+ {
739
+ name: 'uploadDate',
740
+ description: 'The day the video was first published, as YYYY-MM-DD. Search ' + 'engines need this and cannot use the page date instead.',
741
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
742
+ label: 'Publication date'
743
+ },
744
+ {
745
+ name: 'durationSeconds',
746
+ description: 'How long the video runs, in seconds. Filled in from the media ' + 'library when it knows; otherwise type it, or leave it blank.',
747
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
748
+ label: 'Duration (seconds)',
749
+ type: 'number'
750
+ },
751
+ {
752
+ name: 'lightbox',
753
+ description: 'Show the poster as a play button and open the film full size in a ' + 'dialog. Needs a poster image; without one the player stays in the ' + "page. A Wistia video's poster waits for a press either way, and the " + 'video plays in place of the poster when this is off. A Wistia player ' + 'that loads with the page stays in the page.',
754
+ component: Aglyn.FieldComponentType.SWITCH,
755
+ label: 'Open in a lightbox'
756
+ },
757
+ {
758
+ name: 'loadPlayer',
759
+ description: "For a Wistia video: show Wistia's player as soon as the page loads, " + 'instead of a poster to press, to visitors whose privacy choices ' + 'allow analytics. Everyone else still gets the poster. Use it on a ' + 'page built to show this one video, so search engines can find the ' + 'player. The player stays in the page even with the lightbox on.',
760
+ component: Aglyn.FieldComponentType.SWITCH,
761
+ label: 'Load the player with the page'
762
+ },
763
+ {
764
+ name: 'preload',
765
+ description: 'How much to fetch before anyone presses play. Leave unset and a ' + 'video with a poster fetches nothing at all.',
766
+ component: Aglyn.FieldComponentType.SELECT,
767
+ label: 'Preload',
768
+ // Real sentinels, never `''` (AGL-1451): each of these is a member of
769
+ // the declared `VideoPreload` union, so a video switched to Metadata
770
+ // can be switched back.
771
+ options: [
772
+ {
773
+ value: 'none',
774
+ label: 'Nothing until play (default with a poster)'
775
+ },
776
+ {
777
+ value: 'metadata',
778
+ label: 'Length and dimensions only'
779
+ },
780
+ {
781
+ value: 'auto',
782
+ label: 'As much as the browser likes'
783
+ }
784
+ ]
785
+ },
786
+ {
787
+ name: 'controls',
788
+ description: 'Show the browser playback controls (on by default).',
789
+ component: Aglyn.FieldComponentType.CHECKBOX,
790
+ label: 'Controls'
791
+ },
792
+ {
793
+ name: 'autoPlay',
794
+ description: 'Start playback automatically (mutes the video).',
795
+ component: Aglyn.FieldComponentType.CHECKBOX,
796
+ label: 'Autoplay'
797
+ },
798
+ {
799
+ name: 'loop',
800
+ description: 'Restart the video when it ends.',
801
+ component: Aglyn.FieldComponentType.CHECKBOX,
802
+ label: 'Loop'
803
+ },
804
+ {
805
+ name: 'muted',
806
+ description: 'Play without sound.',
807
+ component: Aglyn.FieldComponentType.CHECKBOX,
808
+ label: 'Muted'
809
+ },
810
+ {
811
+ name: 'playsInline',
812
+ description: 'Keep the video in the page on a phone instead of taking over the ' + 'whole screen (on by default).',
813
+ component: Aglyn.FieldComponentType.CHECKBOX,
814
+ label: 'Play inline on phones'
815
+ },
816
+ {
817
+ name: 'captionsSrc',
818
+ description: 'A WebVTT captions file (.vtt) for this video — pick it with ' + '"Browse media" or paste its URL.',
819
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
820
+ label: 'Captions file'
821
+ },
822
+ {
823
+ name: 'captionsLabel',
824
+ description: 'What to call the captions track in the player menu, e.g. English.',
825
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
826
+ label: 'Captions label',
827
+ condition: HAS_CAPTIONS
828
+ },
829
+ {
830
+ name: 'captionsLang',
831
+ description: 'The language code of the captions, e.g. en or es — it tells the ' + 'browser which track to offer.',
832
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
833
+ label: 'Captions language',
834
+ condition: HAS_CAPTIONS
835
+ },
836
+ {
837
+ name: 'width',
838
+ description: 'Width of the player — a number plus a unit, e.g. 100% or 640px.',
839
+ component: Aglyn.FieldComponentType.CSS_DIMENSION,
840
+ label: 'Width'
841
+ },
842
+ {
843
+ name: 'height',
844
+ description: 'Height of the player. Leave empty for auto.',
845
+ component: Aglyn.FieldComponentType.CSS_DIMENSION,
846
+ label: 'Height'
847
+ }
848
+ ]
849
+ };
850
+ export const presets = [
851
+ {
852
+ $id: generatePresetId(ID),
853
+ type: Aglyn.NodeType.PRESET,
854
+ displayName: 'Video',
855
+ pluginId: BUNDLE_ID,
856
+ description: 'Video from your media library or any URL',
857
+ category: Aglyn.ComponentCategory.MEDIA,
858
+ icon: {
859
+ path: mdiVideo.path,
860
+ sx: {
861
+ color: '#7b1fa2'
862
+ }
863
+ },
864
+ data: {
865
+ $id: null,
866
+ componentId: ID,
867
+ pluginId: BUNDLE_ID,
868
+ props: {}
869
+ }
870
+ }
871
+ ];
872
+ export default Video;
873
+
874
+ //# sourceMappingURL=video.js.map