@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.
- package/LICENSE +201 -0
- package/README.md +22 -0
- package/package.json +47 -0
- package/src/index.d.ts +21 -0
- package/src/index.js +22 -0
- package/src/index.js.map +1 -0
- package/src/lib/components/accordion.d.ts +96 -0
- package/src/lib/components/accordion.js +392 -0
- package/src/lib/components/accordion.js.map +1 -0
- package/src/lib/components/app-bar.d.ts +32 -0
- package/src/lib/components/app-bar.js +86 -0
- package/src/lib/components/app-bar.js.map +1 -0
- package/src/lib/components/blocks.d.ts +46 -0
- package/src/lib/components/blocks.js +1007 -0
- package/src/lib/components/blocks.js.map +1 -0
- package/src/lib/components/box.d.ts +47 -0
- package/src/lib/components/box.js +125 -0
- package/src/lib/components/box.js.map +1 -0
- package/src/lib/components/breadcrumbs.d.ts +44 -0
- package/src/lib/components/breadcrumbs.js +172 -0
- package/src/lib/components/breadcrumbs.js.map +1 -0
- package/src/lib/components/button.d.ts +89 -0
- package/src/lib/components/button.js +272 -0
- package/src/lib/components/button.js.map +1 -0
- package/src/lib/components/card.d.ts +61 -0
- package/src/lib/components/card.js +336 -0
- package/src/lib/components/card.js.map +1 -0
- package/src/lib/components/collection.d.ts +514 -0
- package/src/lib/components/collection.js +2999 -0
- package/src/lib/components/collection.js.map +1 -0
- package/src/lib/components/container.d.ts +42 -0
- package/src/lib/components/container.js +169 -0
- package/src/lib/components/container.js.map +1 -0
- package/src/lib/components/custom-html.d.ts +78 -0
- package/src/lib/components/custom-html.js +219 -0
- package/src/lib/components/custom-html.js.map +1 -0
- package/src/lib/components/data-table.d.ts +57 -0
- package/src/lib/components/data-table.js +216 -0
- package/src/lib/components/data-table.js.map +1 -0
- package/src/lib/components/document-root.d.ts +77 -0
- package/src/lib/components/document-root.js +118 -0
- package/src/lib/components/document-root.js.map +1 -0
- package/src/lib/components/drawer.d.ts +93 -0
- package/src/lib/components/drawer.js +566 -0
- package/src/lib/components/drawer.js.map +1 -0
- package/src/lib/components/function-controls.d.ts +66 -0
- package/src/lib/components/function-controls.js +207 -0
- package/src/lib/components/function-controls.js.map +1 -0
- package/src/lib/components/function-scope.d.ts +110 -0
- package/src/lib/components/function-scope.js +478 -0
- package/src/lib/components/function-scope.js.map +1 -0
- package/src/lib/components/function-widget.d.ts +75 -0
- package/src/lib/components/function-widget.js +307 -0
- package/src/lib/components/function-widget.js.map +1 -0
- package/src/lib/components/grid.d.ts +91 -0
- package/src/lib/components/grid.js +322 -0
- package/src/lib/components/grid.js.map +1 -0
- package/src/lib/components/icon.d.ts +54 -0
- package/src/lib/components/icon.js +163 -0
- package/src/lib/components/icon.js.map +1 -0
- package/src/lib/components/image-list.d.ts +58 -0
- package/src/lib/components/image-list.js +336 -0
- package/src/lib/components/image-list.js.map +1 -0
- package/src/lib/components/image.d.ts +154 -0
- package/src/lib/components/image.js +451 -0
- package/src/lib/components/image.js.map +1 -0
- package/src/lib/components/inline-text.d.ts +74 -0
- package/src/lib/components/inline-text.js +407 -0
- package/src/lib/components/inline-text.js.map +1 -0
- package/src/lib/components/language-switcher.d.ts +35 -0
- package/src/lib/components/language-switcher.js +179 -0
- package/src/lib/components/language-switcher.js.map +1 -0
- package/src/lib/components/layout-slot.d.ts +68 -0
- package/src/lib/components/layout-slot.js +192 -0
- package/src/lib/components/layout-slot.js.map +1 -0
- package/src/lib/components/link-box-accessible-name.d.ts +79 -0
- package/src/lib/components/link-box-accessible-name.js +158 -0
- package/src/lib/components/link-box-accessible-name.js.map +1 -0
- package/src/lib/components/link-box.d.ts +66 -0
- package/src/lib/components/link-box.js +212 -0
- package/src/lib/components/link-box.js.map +1 -0
- package/src/lib/components/list-item-text.d.ts +22 -0
- package/src/lib/components/list-item-text.js +86 -0
- package/src/lib/components/list-item-text.js.map +1 -0
- package/src/lib/components/list-item.d.ts +32 -0
- package/src/lib/components/list-item.js +127 -0
- package/src/lib/components/list-item.js.map +1 -0
- package/src/lib/components/list.d.ts +34 -0
- package/src/lib/components/list.js +108 -0
- package/src/lib/components/list.js.map +1 -0
- package/src/lib/components/markdown.d.ts +129 -0
- package/src/lib/components/markdown.js +853 -0
- package/src/lib/components/markdown.js.map +1 -0
- package/src/lib/components/nav-menu.d.ts +68 -0
- package/src/lib/components/nav-menu.js +683 -0
- package/src/lib/components/nav-menu.js.map +1 -0
- package/src/lib/components/pagination.d.ts +59 -0
- package/src/lib/components/pagination.js +243 -0
- package/src/lib/components/pagination.js.map +1 -0
- package/src/lib/components/paper.d.ts +47 -0
- package/src/lib/components/paper.js +202 -0
- package/src/lib/components/paper.js.map +1 -0
- package/src/lib/components/plugin-frame.d.ts +64 -0
- package/src/lib/components/plugin-frame.js +335 -0
- package/src/lib/components/plugin-frame.js.map +1 -0
- package/src/lib/components/plugin.d.ts +95 -0
- package/src/lib/components/plugin.js +304 -0
- package/src/lib/components/plugin.js.map +1 -0
- package/src/lib/components/product.d.ts +40 -0
- package/src/lib/components/product.js +287 -0
- package/src/lib/components/product.js.map +1 -0
- package/src/lib/components/reusable-instance.d.ts +57 -0
- package/src/lib/components/reusable-instance.js +126 -0
- package/src/lib/components/reusable-instance.js.map +1 -0
- package/src/lib/components/screen-link.d.ts +85 -0
- package/src/lib/components/screen-link.js +324 -0
- package/src/lib/components/screen-link.js.map +1 -0
- package/src/lib/components/search-box.d.ts +41 -0
- package/src/lib/components/search-box.js +125 -0
- package/src/lib/components/search-box.js.map +1 -0
- package/src/lib/components/section.d.ts +56 -0
- package/src/lib/components/section.js +132 -0
- package/src/lib/components/section.js.map +1 -0
- package/src/lib/components/stack.d.ts +55 -0
- package/src/lib/components/stack.js +309 -0
- package/src/lib/components/stack.js.map +1 -0
- package/src/lib/components/tabs.d.ts +288 -0
- package/src/lib/components/tabs.js +788 -0
- package/src/lib/components/tabs.js.map +1 -0
- package/src/lib/components/theme-mode-switcher.d.ts +38 -0
- package/src/lib/components/theme-mode-switcher.js +168 -0
- package/src/lib/components/theme-mode-switcher.js.map +1 -0
- package/src/lib/components/toolbar.d.ts +37 -0
- package/src/lib/components/toolbar.js +117 -0
- package/src/lib/components/toolbar.js.map +1 -0
- package/src/lib/components/typography.d.ts +33 -0
- package/src/lib/components/typography.js +433 -0
- package/src/lib/components/typography.js.map +1 -0
- package/src/lib/components/video-lightbox-controls.d.ts +131 -0
- package/src/lib/components/video-lightbox-controls.js +536 -0
- package/src/lib/components/video-lightbox-controls.js.map +1 -0
- package/src/lib/components/video-lightbox.d.ts +60 -0
- package/src/lib/components/video-lightbox.js +247 -0
- package/src/lib/components/video-lightbox.js.map +1 -0
- package/src/lib/components/video-playback-beacon.d.ts +80 -0
- package/src/lib/components/video-playback-beacon.js +128 -0
- package/src/lib/components/video-playback-beacon.js.map +1 -0
- package/src/lib/components/video-player-frame.d.ts +58 -0
- package/src/lib/components/video-player-frame.js +51 -0
- package/src/lib/components/video-player-frame.js.map +1 -0
- package/src/lib/components/video.d.ts +220 -0
- package/src/lib/components/video.js +874 -0
- package/src/lib/components/video.js.map +1 -0
- package/src/lib/constants/bundle-common.d.ts +17 -0
- package/src/lib/constants/bundle-common.js +18 -0
- package/src/lib/constants/bundle-common.js.map +1 -0
- package/src/lib/constants/field-presets.d.ts +93 -0
- package/src/lib/constants/field-presets.js +248 -0
- package/src/lib/constants/field-presets.js.map +1 -0
- package/src/lib/plugin.d.ts +80 -0
- package/src/lib/plugin.js +599 -0
- package/src/lib/plugin.js.map +1 -0
- package/src/lib/utils/author-html-label.d.ts +73 -0
- package/src/lib/utils/author-html-label.js +89 -0
- package/src/lib/utils/author-html-label.js.map +1 -0
- package/src/lib/utils/drop-cleared-props.d.ts +18 -0
- package/src/lib/utils/drop-cleared-props.js +74 -0
- package/src/lib/utils/drop-cleared-props.js.map +1 -0
- package/src/lib/utils/element-picker.d.ts +99 -0
- package/src/lib/utils/element-picker.js +140 -0
- package/src/lib/utils/element-picker.js.map +1 -0
- package/src/lib/utils/generate-preset-id.d.ts +19 -0
- package/src/lib/utils/generate-preset-id.js +25 -0
- package/src/lib/utils/generate-preset-id.js.map +1 -0
- package/src/lib/utils/link-target-props.d.ts +53 -0
- package/src/lib/utils/link-target-props.js +53 -0
- package/src/lib/utils/link-target-props.js.map +1 -0
|
@@ -0,0 +1,433 @@
|
|
|
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 } 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 { mdiAlphabetical, mdiFormatHeader1, mdiFormatHeader2, mdiFormatHeader3, mdiFormatHeader4, mdiFormatHeader5, mdiFormatHeader6, mdiFormatText } from "@aglyn/shared-data-mdi";
|
|
21
|
+
import Typography from "@mui/material/Typography";
|
|
22
|
+
import { forwardRef } from "react";
|
|
23
|
+
import { BUNDLE_ID } from "../constants/bundle-common.js";
|
|
24
|
+
import { FIELD_TEXT_CONTENT } from "../constants/field-presets.js";
|
|
25
|
+
import { dropClearedProps } from "../utils/drop-cleared-props.js";
|
|
26
|
+
import { generatePresetId } from "../utils/generate-preset-id.js";
|
|
27
|
+
// Component ids are persisted in screen documents; keep the legacy ids.
|
|
28
|
+
export const ID = 'muiTypography';
|
|
29
|
+
// The brand's own rungs sit alongside MUI's. They were added to the theme
|
|
30
|
+
// (AGL-1308 for the body scale, for the display rung) but never to
|
|
31
|
+
// this list, so the only way to reach one was to hand-write its pixels into
|
|
32
|
+
// the Styles panel — which is the very thing the tokens exist to stop.
|
|
33
|
+
// Display first: the list reads largest to smallest.
|
|
34
|
+
//
|
|
35
|
+
// Exported because it is the theme's rung list, not this element's: any block
|
|
36
|
+
// that offers a type step has to offer the SAME one, or a rung added to the
|
|
37
|
+
// theme becomes reachable from the Typography element and nowhere else.
|
|
38
|
+
export const typographyVariants = [
|
|
39
|
+
{
|
|
40
|
+
value: 'displayXl',
|
|
41
|
+
label: 'Display XL',
|
|
42
|
+
icon: {
|
|
43
|
+
path: mdiFormatHeader1.path
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
value: 'h1',
|
|
48
|
+
label: 'Heading 1',
|
|
49
|
+
icon: {
|
|
50
|
+
path: mdiFormatHeader1.path
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
value: 'h2',
|
|
55
|
+
label: 'Heading 2',
|
|
56
|
+
icon: {
|
|
57
|
+
path: mdiFormatHeader2.path
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
value: 'h3',
|
|
62
|
+
label: 'Heading 3',
|
|
63
|
+
icon: {
|
|
64
|
+
path: mdiFormatHeader3.path
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
value: 'h4',
|
|
69
|
+
label: 'Heading 4',
|
|
70
|
+
icon: {
|
|
71
|
+
path: mdiFormatHeader4.path
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
value: 'h5',
|
|
76
|
+
label: 'Heading 5',
|
|
77
|
+
icon: {
|
|
78
|
+
path: mdiFormatHeader5.path
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
value: 'h6',
|
|
83
|
+
label: 'Heading 6',
|
|
84
|
+
icon: {
|
|
85
|
+
path: mdiFormatHeader6.path
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
value: 'subtitle1',
|
|
90
|
+
label: 'Subtitle 1',
|
|
91
|
+
icon: {
|
|
92
|
+
path: mdiFormatText.path
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
value: 'subtitle2',
|
|
97
|
+
label: 'Subtitle 2',
|
|
98
|
+
icon: {
|
|
99
|
+
path: mdiFormatText.path
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
value: 'body1',
|
|
104
|
+
label: 'Body 1',
|
|
105
|
+
icon: {
|
|
106
|
+
path: mdiFormatText.path
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
value: 'body2',
|
|
111
|
+
label: 'Body 2',
|
|
112
|
+
icon: {
|
|
113
|
+
path: mdiFormatText.path
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
value: 'lede',
|
|
118
|
+
label: 'Lede',
|
|
119
|
+
icon: {
|
|
120
|
+
path: mdiFormatText.path
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
value: 'overline',
|
|
125
|
+
label: 'Overline',
|
|
126
|
+
icon: {
|
|
127
|
+
path: mdiFormatText.path
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
value: 'caption',
|
|
132
|
+
label: 'Caption',
|
|
133
|
+
icon: {
|
|
134
|
+
path: mdiFormatText.path
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
value: 'bodyCompact',
|
|
139
|
+
label: 'Body compact',
|
|
140
|
+
icon: {
|
|
141
|
+
path: mdiFormatText.path
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
value: 'micro',
|
|
146
|
+
label: 'Micro',
|
|
147
|
+
icon: {
|
|
148
|
+
path: mdiFormatText.path
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
];
|
|
152
|
+
export const schema = {
|
|
153
|
+
$id: ID,
|
|
154
|
+
pluginId: BUNDLE_ID,
|
|
155
|
+
displayName: 'Typography',
|
|
156
|
+
description: "A block of text in one of the theme's type styles.",
|
|
157
|
+
category: Aglyn.ComponentCategory.TEXT,
|
|
158
|
+
icon: {
|
|
159
|
+
path: mdiAlphabetical.path,
|
|
160
|
+
sx: {
|
|
161
|
+
color: '#057822'
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
flags: {
|
|
165
|
+
textEditable: Aglyn.FEATURE_FLAG.ENABLED,
|
|
166
|
+
richTextEditable: Aglyn.FEATURE_FLAG.ENABLED
|
|
167
|
+
},
|
|
168
|
+
attributes: [
|
|
169
|
+
FIELD_TEXT_CONTENT,
|
|
170
|
+
// FIELD_COLOR,
|
|
171
|
+
{
|
|
172
|
+
name: 'variant',
|
|
173
|
+
description: 'Which typography step from the site theme this text uses — h1–h6, ' + 'body, subtitle, caption, overline. It sets size, weight and line ' + 'height together; it does NOT change the HTML element, which is the ' + 'field below.',
|
|
174
|
+
component: Aglyn.FieldComponentType.SELECT,
|
|
175
|
+
label: 'Variant',
|
|
176
|
+
// "Default" deleted (AGL-1453): unpersistable, and a second name for
|
|
177
|
+
// `body1`, MUI's own default, already in `typographyVariants`.
|
|
178
|
+
options: [
|
|
179
|
+
...typographyVariants
|
|
180
|
+
]
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: 'component',
|
|
184
|
+
description: 'The html element to use.',
|
|
185
|
+
component: Aglyn.FieldComponentType.SELECT,
|
|
186
|
+
label: 'Component',
|
|
187
|
+
// "Default" deleted rather than given a sentinel (AGL-1453), and this
|
|
188
|
+
// is the prop that shows why a sentinel is not always available: MUI
|
|
189
|
+
// derives the element from `variantMapping[variant]`, so "Default"
|
|
190
|
+
// resolves to a DIFFERENT tag per variant — `h2` for a Heading 2, `p`
|
|
191
|
+
// for Body 1. No single value on this list expresses that, and picking
|
|
192
|
+
// one would pin the tag and change the document outline. Unset is the
|
|
193
|
+
// only thing that means "let the variant decide", and the field's ✕ is
|
|
194
|
+
// how an author says it.
|
|
195
|
+
options: [
|
|
196
|
+
{
|
|
197
|
+
value: 'h1',
|
|
198
|
+
label: 'Heading 1'
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
value: 'h2',
|
|
202
|
+
label: 'Heading 2'
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
value: 'h3',
|
|
206
|
+
label: 'Heading 3'
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
value: 'h4',
|
|
210
|
+
label: 'Heading 4'
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
value: 'h5',
|
|
214
|
+
label: 'Heading 5'
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
value: 'h6',
|
|
218
|
+
label: 'Heading 6'
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
value: 'p',
|
|
222
|
+
label: 'Paragraph'
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
value: 'div',
|
|
226
|
+
label: 'Div'
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
value: 'span',
|
|
230
|
+
label: 'Span'
|
|
231
|
+
}
|
|
232
|
+
]
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: 'align',
|
|
236
|
+
description: 'The text alignment',
|
|
237
|
+
component: Aglyn.FieldComponentType.SELECT,
|
|
238
|
+
label: 'Alignment',
|
|
239
|
+
// "Default" deleted (AGL-1453): unpersistable, and a second name for
|
|
240
|
+
// `inherit`, MUI Typography's own default, already on the list.
|
|
241
|
+
options: [
|
|
242
|
+
{
|
|
243
|
+
value: 'inherit',
|
|
244
|
+
label: 'Inherit'
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
value: 'left',
|
|
248
|
+
label: 'Left'
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
value: 'center',
|
|
252
|
+
label: 'Center'
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
value: 'right',
|
|
256
|
+
label: 'Right'
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
value: 'justify',
|
|
260
|
+
label: 'Justified'
|
|
261
|
+
}
|
|
262
|
+
]
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: 'noWrap',
|
|
266
|
+
description: 'If true, the text will not wrap/fold.',
|
|
267
|
+
component: Aglyn.FieldComponentType.SWITCH,
|
|
268
|
+
label: 'Disable wrapping?'
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
name: 'gutterBottom',
|
|
272
|
+
description: 'If true, the text will have a space beneath.',
|
|
273
|
+
component: Aglyn.FieldComponentType.SWITCH,
|
|
274
|
+
label: 'Gutter bottom?'
|
|
275
|
+
}
|
|
276
|
+
]
|
|
277
|
+
};
|
|
278
|
+
export const presets = [
|
|
279
|
+
...typographyVariants.map((item)=>({
|
|
280
|
+
$id: generatePresetId(ID, item.value),
|
|
281
|
+
type: Aglyn.NodeType.PRESET,
|
|
282
|
+
displayName: item.label,
|
|
283
|
+
pluginId: BUNDLE_ID,
|
|
284
|
+
description: `Element with ${item.label} styles`,
|
|
285
|
+
category: Aglyn.ComponentCategory.TEXT,
|
|
286
|
+
icon: _extends({
|
|
287
|
+
sx: {
|
|
288
|
+
color: '#057822'
|
|
289
|
+
}
|
|
290
|
+
}, item.icon),
|
|
291
|
+
data: {
|
|
292
|
+
$id: null,
|
|
293
|
+
componentId: ID,
|
|
294
|
+
pluginId: BUNDLE_ID,
|
|
295
|
+
props: {
|
|
296
|
+
variant: item.value,
|
|
297
|
+
children: item.label
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}))
|
|
301
|
+
];
|
|
302
|
+
/**
|
|
303
|
+
* Typography that renders rich text (AGL-54): when the node carries an
|
|
304
|
+
* `html` prop it renders as innerHTML; otherwise plain `children`.
|
|
305
|
+
*
|
|
306
|
+
* The `html` prop is re-sanitized on EVERY render (AGL-497). The inline
|
|
307
|
+
* editor sanitizes at commit, but screen node props are written directly via
|
|
308
|
+
* the Firebase client SDK, so a host editor can plant arbitrary `html`
|
|
309
|
+
* (bypassing the editor) that would execute on the public site AND in the
|
|
310
|
+
* besigner canvas on app.aglyn.com.
|
|
311
|
+
*
|
|
312
|
+
* The sanitizer is `Aglyn.sanitizeAuthorHtml` (AGL-1901), which needs no DOM
|
|
313
|
+
* and so runs on the server too — the DOMPurify call this replaced could
|
|
314
|
+
* not, which is why the content used to be deferred to an effect and left
|
|
315
|
+
* every rich-text body out of the server response. The allowlist it enforces
|
|
316
|
+
* is a subset of the old DOMPurify config's, pinned pair by pair in
|
|
317
|
+
* `rich-text-hydration.spec.tsx`.
|
|
318
|
+
*/ function sanitizeTypographyHtml(html) {
|
|
319
|
+
return Aglyn.sanitizeAuthorHtml(html);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* MUI's own `defaultVariantMapping`, plus its `span` fallback — the element
|
|
323
|
+
* Typography will actually render for a given variant. Copied rather than
|
|
324
|
+
* imported because MUI does not export it; `author-html-round-trip.spec.tsx`
|
|
325
|
+
* asserts this table still matches what MUI actually renders, so a MUI upgrade that
|
|
326
|
+
* changed the mapping fails there rather than silently reopening the
|
|
327
|
+
* reparenting hazard below.
|
|
328
|
+
*/ /**
|
|
329
|
+
* The element each variant renders as, and the ONE place that decides
|
|
330
|
+
*.
|
|
331
|
+
*
|
|
332
|
+
* MUI's own `variantMapping` sends `subtitle1` and `subtitle2` to `<h6>`.
|
|
333
|
+
* That is a typographic choice made in a component library, and on a page
|
|
334
|
+
* builder it is a document-outline decision made by somebody who has never
|
|
335
|
+
* seen the page: a subtitle is a label, not a section, and every one on every
|
|
336
|
+
* Aglyn site was silently inserting an `<h6>` into the heading order. It is
|
|
337
|
+
* what Lighthouse's `heading-order` was reporting — a "Open source &
|
|
338
|
+
* self-hostable" card label rendering ahead of the page's own `<h1>`, from a
|
|
339
|
+
* node whose author had selected Subtitle 1 and nothing else.
|
|
340
|
+
*
|
|
341
|
+
* Subtitles render as `<p>` here. An author who wants a heading still says so
|
|
342
|
+
* with the Component field, which is the control for exactly that question
|
|
343
|
+
* and is unchanged.
|
|
344
|
+
*
|
|
345
|
+
* Passed to MUI as `variantMapping` rather than only consulted by
|
|
346
|
+
* `blockSafeComponent`, so the table cannot describe one thing while the
|
|
347
|
+
* element is another.
|
|
348
|
+
*/ const VARIANT_ELEMENT = {
|
|
349
|
+
h1: 'h1',
|
|
350
|
+
h2: 'h2',
|
|
351
|
+
h3: 'h3',
|
|
352
|
+
h4: 'h4',
|
|
353
|
+
h5: 'h5',
|
|
354
|
+
h6: 'h6',
|
|
355
|
+
subtitle1: 'p',
|
|
356
|
+
subtitle2: 'p',
|
|
357
|
+
body1: 'p',
|
|
358
|
+
body2: 'p',
|
|
359
|
+
inherit: 'p'
|
|
360
|
+
};
|
|
361
|
+
/**
|
|
362
|
+
* The element the rich-text body is put INSIDE, which may have to be a
|
|
363
|
+
* `<div>` rather than the one the variant implies (AGL-1901).
|
|
364
|
+
*
|
|
365
|
+
* The parser closes a `<p>` on any block start tag, and a heading on any
|
|
366
|
+
* other heading. So a body with an `<h2>` in it, served inside the `<p>` MUI
|
|
367
|
+
* renders for `body1`, arrives at the browser as
|
|
368
|
+
* `<p class="MuiTypography-root"></p><h2>…</h2>` — the container emptied and
|
|
369
|
+
* the body promoted to a sibling. React then hydrates against a DOM that is
|
|
370
|
+
* not the one it described and reports a mismatch: the live React #418 shape
|
|
371
|
+
* on tenant (AGL-1926), which this change must not add to.
|
|
372
|
+
*
|
|
373
|
+
* This never mattered before because the server `<p>` was EMPTY and the body
|
|
374
|
+
* arrived through an `innerHTML` assignment, which parses in the CONTEXT of
|
|
375
|
+
* the `<p>` and so does not reparent. Serving the body is what makes the
|
|
376
|
+
* container's element type load-bearing.
|
|
377
|
+
*
|
|
378
|
+
* `authorHtmlBreaksContainer` holds the rule, next to the table of tags it
|
|
379
|
+
* depends on. Returns `'div'` to override, or `undefined` to leave MUI's
|
|
380
|
+
* choice alone; the decision is a pure function of props, so the server and
|
|
381
|
+
* the client make it identically.
|
|
382
|
+
*/ function blockSafeComponent(sanitized, rest) {
|
|
383
|
+
var _ref, _rest_component, _rest_variant;
|
|
384
|
+
const element = (_ref = (_rest_component = rest.component) != null ? _rest_component : // MUI's own default variant is `body1`, which maps to `p` — the case that
|
|
385
|
+
// matters, since a Typography with no variant set is the shape nearly
|
|
386
|
+
// every rich-text node has. (`paragraph` is gone in MUI v7, so the
|
|
387
|
+
// variant mapping is the only other input.)
|
|
388
|
+
VARIANT_ELEMENT[(_rest_variant = rest.variant) != null ? _rest_variant : 'body1']) != null ? _ref : 'span';
|
|
389
|
+
return Aglyn.authorHtmlBreaksContainer(sanitized, element) ? 'div' : undefined;
|
|
390
|
+
}
|
|
391
|
+
const AglynTypography = /*#__PURE__*/ forwardRef(function AglynTypography(props, ref) {
|
|
392
|
+
const { html, children } = props, spread = _object_without_properties_loose(props, [
|
|
393
|
+
"html",
|
|
394
|
+
"children"
|
|
395
|
+
]);
|
|
396
|
+
// A cleared `align` persists as null and MUI capitalizes it — an SSR throw
|
|
397
|
+
// that 500s the page (AGL-1226, same shape as the button colour).
|
|
398
|
+
const rest = dropClearedProps(spread);
|
|
399
|
+
const hasHtml = typeof html === 'string' && Boolean(html);
|
|
400
|
+
// Sanitized DURING render, on the server and in the browser alike
|
|
401
|
+
// (AGL-1901). The effect this replaced existed because DOMPurify needs a
|
|
402
|
+
// DOM: the server rendered nothing, so the first client render had to
|
|
403
|
+
// render nothing too or hydration would mismatch (AGL-1268) — the two
|
|
404
|
+
// sides agreed only by both being empty, and the body never reached a
|
|
405
|
+
// crawler. `sanitizeAuthorHtml` is a pure function of the string with no
|
|
406
|
+
// DOM dependency, so both sides now compute the same bytes from the same
|
|
407
|
+
// prop and agree on the CONTENT instead of on the absence of it.
|
|
408
|
+
const sanitized = hasHtml ? sanitizeTypographyHtml(html) : null;
|
|
409
|
+
if (sanitized !== null) {
|
|
410
|
+
// A `<p>` cannot hold the block content a rich-text body may contain —
|
|
411
|
+
// see `blockSafeComponent`. `undefined` leaves MUI's own choice alone.
|
|
412
|
+
const component = blockSafeComponent(sanitized, rest);
|
|
413
|
+
return /*#__PURE__*/ _jsx(Typography, _extends({
|
|
414
|
+
ref: ref,
|
|
415
|
+
variantMapping: VARIANT_ELEMENT
|
|
416
|
+
}, rest, component ? {
|
|
417
|
+
component
|
|
418
|
+
} : {}, {
|
|
419
|
+
dangerouslySetInnerHTML: {
|
|
420
|
+
__html: sanitized
|
|
421
|
+
}
|
|
422
|
+
}));
|
|
423
|
+
}
|
|
424
|
+
return /*#__PURE__*/ _jsx(Typography, _extends({
|
|
425
|
+
ref: ref,
|
|
426
|
+
variantMapping: VARIANT_ELEMENT
|
|
427
|
+
}, rest, {
|
|
428
|
+
children: children
|
|
429
|
+
}));
|
|
430
|
+
});
|
|
431
|
+
export default AglynTypography;
|
|
432
|
+
|
|
433
|
+
//# sourceMappingURL=typography.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/typography.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport {\n mdiAlphabetical,\n mdiFormatHeader1,\n mdiFormatHeader2,\n mdiFormatHeader3,\n mdiFormatHeader4,\n mdiFormatHeader5,\n mdiFormatHeader6,\n mdiFormatText,\n} from '@aglyn/shared-data-mdi'\nimport Typography, { type TypographyProps } from '@mui/material/Typography'\nimport { forwardRef } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { FIELD_TEXT_CONTENT } from '../constants/field-presets'\nimport { dropClearedProps } from '../utils/drop-cleared-props'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; keep the legacy ids.\nexport const ID: Aglyn.ComponentId = 'muiTypography'\n// The brand's own rungs sit alongside MUI's. They were added to the theme\n// (AGL-1308 for the body scale, for the display rung) but never to\n// this list, so the only way to reach one was to hand-write its pixels into\n// the Styles panel — which is the very thing the tokens exist to stop.\n// Display first: the list reads largest to smallest.\n//\n// Exported because it is the theme's rung list, not this element's: any block\n// that offers a type step has to offer the SAME one, or a rung added to the\n// theme becomes reachable from the Typography element and nowhere else.\nexport const typographyVariants = [\n {\n value: 'displayXl',\n label: 'Display XL',\n icon: { path: mdiFormatHeader1.path },\n },\n { value: 'h1', label: 'Heading 1', icon: { path: mdiFormatHeader1.path } },\n { value: 'h2', label: 'Heading 2', icon: { path: mdiFormatHeader2.path } },\n { value: 'h3', label: 'Heading 3', icon: { path: mdiFormatHeader3.path } },\n { value: 'h4', label: 'Heading 4', icon: { path: mdiFormatHeader4.path } },\n { value: 'h5', label: 'Heading 5', icon: { path: mdiFormatHeader5.path } },\n { value: 'h6', label: 'Heading 6', icon: { path: mdiFormatHeader6.path } },\n {\n value: 'subtitle1',\n label: 'Subtitle 1',\n icon: { path: mdiFormatText.path },\n },\n {\n value: 'subtitle2',\n label: 'Subtitle 2',\n icon: { path: mdiFormatText.path },\n },\n { value: 'body1', label: 'Body 1', icon: { path: mdiFormatText.path } },\n { value: 'body2', label: 'Body 2', icon: { path: mdiFormatText.path } },\n { value: 'lede', label: 'Lede', icon: { path: mdiFormatText.path } },\n { value: 'overline', label: 'Overline', icon: { path: mdiFormatText.path } },\n { value: 'caption', label: 'Caption', icon: { path: mdiFormatText.path } },\n {\n value: 'bodyCompact',\n label: 'Body compact',\n icon: { path: mdiFormatText.path },\n },\n { value: 'micro', label: 'Micro', icon: { path: mdiFormatText.path } },\n]\n\nexport const schema: Aglyn.ComponentSchema = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Typography',\n description: \"A block of text in one of the theme's type styles.\",\n category: Aglyn.ComponentCategory.TEXT,\n icon: {\n path: mdiAlphabetical.path,\n sx: { color: '#057822' },\n },\n flags: {\n textEditable: Aglyn.FEATURE_FLAG.ENABLED,\n richTextEditable: Aglyn.FEATURE_FLAG.ENABLED,\n },\n attributes: [\n FIELD_TEXT_CONTENT,\n // FIELD_COLOR,\n {\n name: 'variant',\n description:\n 'Which typography step from the site theme this text uses — h1–h6, ' +\n 'body, subtitle, caption, overline. It sets size, weight and line ' +\n 'height together; it does NOT change the HTML element, which is the ' +\n 'field below.',\n component: Aglyn.FieldComponentType.SELECT,\n label: 'Variant',\n // \"Default\" deleted (AGL-1453): unpersistable, and a second name for\n // `body1`, MUI's own default, already in `typographyVariants`.\n options: [...typographyVariants],\n },\n {\n name: 'component',\n description: 'The html element to use.',\n component: Aglyn.FieldComponentType.SELECT,\n label: 'Component',\n // \"Default\" deleted rather than given a sentinel (AGL-1453), and this\n // is the prop that shows why a sentinel is not always available: MUI\n // derives the element from `variantMapping[variant]`, so \"Default\"\n // resolves to a DIFFERENT tag per variant — `h2` for a Heading 2, `p`\n // for Body 1. No single value on this list expresses that, and picking\n // one would pin the tag and change the document outline. Unset is the\n // only thing that means \"let the variant decide\", and the field's ✕ is\n // how an author says it.\n options: [\n { value: 'h1', label: 'Heading 1' },\n { value: 'h2', label: 'Heading 2' },\n { value: 'h3', label: 'Heading 3' },\n { value: 'h4', label: 'Heading 4' },\n { value: 'h5', label: 'Heading 5' },\n { value: 'h6', label: 'Heading 6' },\n { value: 'p', label: 'Paragraph' },\n { value: 'div', label: 'Div' },\n { value: 'span', label: 'Span' },\n ],\n },\n {\n name: 'align',\n description: 'The text alignment',\n component: Aglyn.FieldComponentType.SELECT,\n label: 'Alignment',\n // \"Default\" deleted (AGL-1453): unpersistable, and a second name for\n // `inherit`, MUI Typography's own default, already on the list.\n options: [\n { value: 'inherit', label: 'Inherit' },\n { value: 'left', label: 'Left' },\n { value: 'center', label: 'Center' },\n { value: 'right', label: 'Right' },\n { value: 'justify', label: 'Justified' },\n ],\n },\n {\n name: 'noWrap',\n description: 'If true, the text will not wrap/fold.',\n component: Aglyn.FieldComponentType.SWITCH,\n label: 'Disable wrapping?',\n },\n {\n name: 'gutterBottom',\n description: 'If true, the text will have a space beneath.',\n component: Aglyn.FieldComponentType.SWITCH,\n label: 'Gutter bottom?',\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n ...typographyVariants.map((item): Aglyn.PresetSchema => ({\n $id: generatePresetId(ID, item.value),\n type: Aglyn.NodeType.PRESET,\n displayName: item.label,\n pluginId: BUNDLE_ID,\n description: `Element with ${item.label} styles`,\n category: Aglyn.ComponentCategory.TEXT,\n icon: {\n sx: { color: '#057822' },\n ...item.icon,\n },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n variant: item.value,\n children: item.label,\n },\n },\n })),\n]\n\n/**\n * Typography that renders rich text (AGL-54): when the node carries an\n * `html` prop it renders as innerHTML; otherwise plain `children`.\n *\n * The `html` prop is re-sanitized on EVERY render (AGL-497). The inline\n * editor sanitizes at commit, but screen node props are written directly via\n * the Firebase client SDK, so a host editor can plant arbitrary `html`\n * (bypassing the editor) that would execute on the public site AND in the\n * besigner canvas on app.aglyn.com.\n *\n * The sanitizer is `Aglyn.sanitizeAuthorHtml` (AGL-1901), which needs no DOM\n * and so runs on the server too — the DOMPurify call this replaced could\n * not, which is why the content used to be deferred to an effect and left\n * every rich-text body out of the server response. The allowlist it enforces\n * is a subset of the old DOMPurify config's, pinned pair by pair in\n * `rich-text-hydration.spec.tsx`.\n */\nfunction sanitizeTypographyHtml(html: string): string {\n return Aglyn.sanitizeAuthorHtml(html)\n}\n\n/**\n * MUI's own `defaultVariantMapping`, plus its `span` fallback — the element\n * Typography will actually render for a given variant. Copied rather than\n * imported because MUI does not export it; `author-html-round-trip.spec.tsx`\n * asserts this table still matches what MUI actually renders, so a MUI upgrade that\n * changed the mapping fails there rather than silently reopening the\n * reparenting hazard below.\n */\n/**\n * The element each variant renders as, and the ONE place that decides\n *.\n *\n * MUI's own `variantMapping` sends `subtitle1` and `subtitle2` to `<h6>`.\n * That is a typographic choice made in a component library, and on a page\n * builder it is a document-outline decision made by somebody who has never\n * seen the page: a subtitle is a label, not a section, and every one on every\n * Aglyn site was silently inserting an `<h6>` into the heading order. It is\n * what Lighthouse's `heading-order` was reporting — a \"Open source &\n * self-hostable\" card label rendering ahead of the page's own `<h1>`, from a\n * node whose author had selected Subtitle 1 and nothing else.\n *\n * Subtitles render as `<p>` here. An author who wants a heading still says so\n * with the Component field, which is the control for exactly that question\n * and is unchanged.\n *\n * Passed to MUI as `variantMapping` rather than only consulted by\n * `blockSafeComponent`, so the table cannot describe one thing while the\n * element is another.\n */\nconst VARIANT_ELEMENT: Record<string, string> = {\n h1: 'h1', h2: 'h2', h3: 'h3', h4: 'h4', h5: 'h5', h6: 'h6',\n subtitle1: 'p', subtitle2: 'p', body1: 'p', body2: 'p', inherit: 'p',\n}\n\n/**\n * The element the rich-text body is put INSIDE, which may have to be a\n * `<div>` rather than the one the variant implies (AGL-1901).\n *\n * The parser closes a `<p>` on any block start tag, and a heading on any\n * other heading. So a body with an `<h2>` in it, served inside the `<p>` MUI\n * renders for `body1`, arrives at the browser as\n * `<p class=\"MuiTypography-root\"></p><h2>…</h2>` — the container emptied and\n * the body promoted to a sibling. React then hydrates against a DOM that is\n * not the one it described and reports a mismatch: the live React #418 shape\n * on tenant (AGL-1926), which this change must not add to.\n *\n * This never mattered before because the server `<p>` was EMPTY and the body\n * arrived through an `innerHTML` assignment, which parses in the CONTEXT of\n * the `<p>` and so does not reparent. Serving the body is what makes the\n * container's element type load-bearing.\n *\n * `authorHtmlBreaksContainer` holds the rule, next to the table of tags it\n * depends on. Returns `'div'` to override, or `undefined` to leave MUI's\n * choice alone; the decision is a pure function of props, so the server and\n * the client make it identically.\n */\nfunction blockSafeComponent(\n sanitized: string,\n rest: TypographyProps,\n): 'div' | undefined {\n const element =\n (rest.component as string | undefined) ??\n // MUI's own default variant is `body1`, which maps to `p` — the case that\n // matters, since a Typography with no variant set is the shape nearly\n // every rich-text node has. (`paragraph` is gone in MUI v7, so the\n // variant mapping is the only other input.)\n VARIANT_ELEMENT[(rest.variant as string) ?? 'body1'] ??\n 'span'\n return Aglyn.authorHtmlBreaksContainer(sanitized, element) ? 'div' : undefined\n}\n\nconst AglynTypography = forwardRef<\n HTMLElement,\n TypographyProps & { html?: string }\n>(function AglynTypography(props, ref) {\n const { html, children, ...spread } = props\n // A cleared `align` persists as null and MUI capitalizes it — an SSR throw\n // that 500s the page (AGL-1226, same shape as the button colour).\n const rest = dropClearedProps(spread)\n const hasHtml = typeof html === 'string' && Boolean(html)\n // Sanitized DURING render, on the server and in the browser alike\n // (AGL-1901). The effect this replaced existed because DOMPurify needs a\n // DOM: the server rendered nothing, so the first client render had to\n // render nothing too or hydration would mismatch (AGL-1268) — the two\n // sides agreed only by both being empty, and the body never reached a\n // crawler. `sanitizeAuthorHtml` is a pure function of the string with no\n // DOM dependency, so both sides now compute the same bytes from the same\n // prop and agree on the CONTENT instead of on the absence of it.\n const sanitized = hasHtml ? sanitizeTypographyHtml(html as string) : null\n if (sanitized !== null) {\n // A `<p>` cannot hold the block content a rich-text body may contain —\n // see `blockSafeComponent`. `undefined` leaves MUI's own choice alone.\n const component = blockSafeComponent(sanitized, rest as TypographyProps)\n return (\n <Typography\n ref={ref}\n variantMapping={VARIANT_ELEMENT}\n {...rest}\n {...(component ? { component } : {})}\n dangerouslySetInnerHTML={{ __html: sanitized }}\n />\n )\n }\n return (\n <Typography ref={ref} variantMapping={VARIANT_ELEMENT} {...rest}>\n {children}\n </Typography>\n )\n})\n\nexport default AglynTypography\n"],"names":["Aglyn","mdiAlphabetical","mdiFormatHeader1","mdiFormatHeader2","mdiFormatHeader3","mdiFormatHeader4","mdiFormatHeader5","mdiFormatHeader6","mdiFormatText","Typography","forwardRef","BUNDLE_ID","FIELD_TEXT_CONTENT","dropClearedProps","generatePresetId","ID","typographyVariants","value","label","icon","path","schema","$id","pluginId","displayName","description","category","ComponentCategory","TEXT","sx","color","flags","textEditable","FEATURE_FLAG","ENABLED","richTextEditable","attributes","name","component","FieldComponentType","SELECT","options","SWITCH","presets","map","item","type","NodeType","PRESET","data","componentId","props","variant","children","sanitizeTypographyHtml","html","sanitizeAuthorHtml","VARIANT_ELEMENT","h1","h2","h3","h4","h5","h6","subtitle1","subtitle2","body1","body2","inherit","blockSafeComponent","sanitized","rest","element","authorHtmlBreaksContainer","undefined","AglynTypography","ref","spread","hasHtml","Boolean","variantMapping","dangerouslySetInnerHTML","__html"],"mappings":"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"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type Theme } from '@mui/material/styles';
|
|
18
|
+
import { type KeyboardEvent, type RefObject } from 'react';
|
|
19
|
+
/** One arrow press on the playhead, on the seek slider or from any other stop. */
|
|
20
|
+
export declare const SEEK_STEP_SECONDS = 5;
|
|
21
|
+
/** `Page Up` and `Page Down` on the seek slider, and an arrow held with `Shift`. */
|
|
22
|
+
export declare const SEEK_PAGE_SECONDS = 10;
|
|
23
|
+
/**
|
|
24
|
+
* A player clock: `m:ss`, or `h:mm:ss` from an hour up.
|
|
25
|
+
*
|
|
26
|
+
* A duration the browser does not know yet (`NaN`, before metadata) or cannot
|
|
27
|
+
* know (`Infinity`, a stream) reads as `0:00` rather than as `NaN:NaN`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatMediaTime(seconds: number): string;
|
|
30
|
+
/** What the seek slider says its value is: `1:23 of 4:10`. */
|
|
31
|
+
export declare function seekValueText(position: number, duration: number): string;
|
|
32
|
+
/** The two elements the controls and the keyboard act on. */
|
|
33
|
+
export interface LightboxPlayerElements {
|
|
34
|
+
video: HTMLVideoElement | null;
|
|
35
|
+
/**
|
|
36
|
+
* The frame that goes full screen: the film AND these controls, so they are
|
|
37
|
+
* still on screen, and still reachable from the keyboard, once it has.
|
|
38
|
+
*/
|
|
39
|
+
player: HTMLElement | null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Play or pause.
|
|
43
|
+
*
|
|
44
|
+
* `play()` rejects when the browser refuses playback, and the refusal needs
|
|
45
|
+
* no handling: no `play` event fires, so the control goes on reporting a
|
|
46
|
+
* paused film, which is the truth.
|
|
47
|
+
*/
|
|
48
|
+
export declare function togglePlayback(video: HTMLVideoElement): void;
|
|
49
|
+
/**
|
|
50
|
+
* Moves the playhead, clamped to the film, and returns where it landed.
|
|
51
|
+
* Before the duration is known there is nowhere to land, and nothing moves.
|
|
52
|
+
*/
|
|
53
|
+
export declare function seekPlayback(video: HTMLVideoElement, seconds: number): number | undefined;
|
|
54
|
+
export declare function toggleMute(video: HTMLVideoElement): void;
|
|
55
|
+
/**
|
|
56
|
+
* Whether a full-screen control would do anything here.
|
|
57
|
+
*
|
|
58
|
+
* `fullscreenEnabled` is false inside a frame whose embedder did not allow it,
|
|
59
|
+
* and there the control is left out rather than shown doing nothing. A phone
|
|
60
|
+
* with no element full screen at all still gets the video-only kind, which
|
|
61
|
+
* plays under the platform's own controls.
|
|
62
|
+
*/
|
|
63
|
+
export declare function fullscreenAvailable(): boolean;
|
|
64
|
+
export declare function toggleFullscreen({ video, player }: LightboxPlayerElements): void;
|
|
65
|
+
/**
|
|
66
|
+
* Whether the focused element uses `key` for itself, in which case no shortcut
|
|
67
|
+
* may take it: `Space` and `Enter` press a focused button, the arrows and
|
|
68
|
+
* `Home`/`End` move a focused slider, and a text field wants every key.
|
|
69
|
+
*/
|
|
70
|
+
export declare function keyBelongsToFocus(target: EventTarget | null, key: string): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* The dialog's `onKeyDown`: the player's shortcuts, from every stop inside it.
|
|
73
|
+
*
|
|
74
|
+
* Bound on the Modal root rather than on the controls, because focus is not
|
|
75
|
+
* always on a control — a click on the film leaves it on the dialog container
|
|
76
|
+
* — and a key pressed anywhere in the dialog bubbles through the root. MUI
|
|
77
|
+
* calls this before its own `Escape` handling, and nothing here touches
|
|
78
|
+
* `Escape`.
|
|
79
|
+
*
|
|
80
|
+
* A key the focused control uses for itself is left alone, and so is any key
|
|
81
|
+
* a control already claimed: MUI's Slider calls `preventDefault` on the keys
|
|
82
|
+
* it moves with, which is how an arrow on a slider stays that slider's.
|
|
83
|
+
*/
|
|
84
|
+
export declare function handlePlayerShortcut(event: KeyboardEvent<Element>, { video, player }: LightboxPlayerElements): void;
|
|
85
|
+
/**
|
|
86
|
+
* Every button on the player, the dialog's close button included.
|
|
87
|
+
*
|
|
88
|
+
* IconButton's own hover rule reads `--IconButton-hoverBg`, `hover: none`
|
|
89
|
+
* reset and all; its default is a tint of `action.active`, which on a light
|
|
90
|
+
* site's palette is black on this black ground and never shows. The ripple is
|
|
91
|
+
* motion, so it goes for a visitor who asked for less, and it is gated in CSS
|
|
92
|
+
* as the poster's play badge is so the setting resolves in both directions.
|
|
93
|
+
*/
|
|
94
|
+
export declare const controlButtonSx: (theme: Theme) => {
|
|
95
|
+
color: string;
|
|
96
|
+
'--IconButton-hoverBg': string;
|
|
97
|
+
'&:focus-visible': {
|
|
98
|
+
outline: string;
|
|
99
|
+
outlineColor: string;
|
|
100
|
+
outlineOffset: number;
|
|
101
|
+
};
|
|
102
|
+
'@media (prefers-reduced-motion: reduce)': {
|
|
103
|
+
[x: string]: {
|
|
104
|
+
display: string;
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
export interface VideoLightboxControlsProps {
|
|
109
|
+
videoRef: RefObject<HTMLVideoElement | null>;
|
|
110
|
+
/** See {@link LightboxPlayerElements.player}. */
|
|
111
|
+
playerRef: RefObject<HTMLElement | null>;
|
|
112
|
+
/** The film carries a captions track, so a toggle for it belongs here. */
|
|
113
|
+
captions?: boolean;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The bar under the film: a seek slider across the top, then play, the clock,
|
|
117
|
+
* mute, volume, captions and full screen.
|
|
118
|
+
*
|
|
119
|
+
* Each control is a real `<button>` or a real range input with a name and a
|
|
120
|
+
* state: `aria-pressed` on the toggles, a spoken `1:23 of 4:10` on the seek
|
|
121
|
+
* slider, `aria-keyshortcuts` where a key does the same thing.
|
|
122
|
+
*
|
|
123
|
+
* What the controls draw is read back off the `<video>` on its own events
|
|
124
|
+
* rather than kept beside it, so a button, a shortcut, a click on the film and
|
|
125
|
+
* the platform's media keys all end in one place, and the play beacon's
|
|
126
|
+
* handlers on the element hear every one of them.
|
|
127
|
+
*/
|
|
128
|
+
export declare function VideoLightboxControls(props: VideoLightboxControlsProps): import("react").JSX.Element;
|
|
129
|
+
export declare namespace VideoLightboxControls {
|
|
130
|
+
var displayName: string;
|
|
131
|
+
}
|