@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,202 @@
|
|
|
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 { mdiFileDocumentOutline } from "@aglyn/shared-data-mdi";
|
|
21
|
+
import MuiPaper from "@mui/material/Paper";
|
|
22
|
+
import { forwardRef } from "react";
|
|
23
|
+
import { BUNDLE_ID } from "../constants/bundle-common.js";
|
|
24
|
+
import { applySemanticElement, semanticElementAttribute, semanticElementLabelAttribute } from "../utils/element-picker.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; never rename.
|
|
28
|
+
export const ID = 'muiPaper';
|
|
29
|
+
/** MUI's shadow scale tops out at 24; anything above renders unshadowed. */ export const MAX_ELEVATION = 24;
|
|
30
|
+
/**
|
|
31
|
+
* Coerces a persisted elevation to the number MUI's shadow lookup needs.
|
|
32
|
+
*
|
|
33
|
+
* Number-typed attribute fields round-trip through the document as
|
|
34
|
+
* strings often enough that this can't be assumed away: `elevation="3"`
|
|
35
|
+
* indexes MUI's shadow array with a string and yields `undefined`, i.e. a
|
|
36
|
+
* paper the author gave a shadow renders completely flat.
|
|
37
|
+
*/ export function toElevation(value) {
|
|
38
|
+
if (value === '' || value == null) return undefined;
|
|
39
|
+
const parsed = Number(value);
|
|
40
|
+
if (!Number.isFinite(parsed)) return undefined;
|
|
41
|
+
return Math.min(Math.max(Math.round(parsed), 0), MAX_ELEVATION);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Surface primitive (https://mui.com/material-ui/react-paper/): the
|
|
45
|
+
* themed background + shadow that Card, Accordion and Drawer are all
|
|
46
|
+
* built on, exposed on its own for panels and callouts.
|
|
47
|
+
*/ const PaperElement = /*#__PURE__*/ forwardRef((rawProps, ref)=>{
|
|
48
|
+
// Cleared props dropped before anything reads them (AGL-1451): `square`
|
|
49
|
+
// and everything else in `rest` spread straight into MUI, where a `null`
|
|
50
|
+
// from the field's ✕ (AGL-1226) is a value rather than an absence.
|
|
51
|
+
const props = dropClearedProps(rawProps);
|
|
52
|
+
const { elevation, variant, children } = props, rest = _object_without_properties_loose(props, [
|
|
53
|
+
"elevation",
|
|
54
|
+
"variant",
|
|
55
|
+
"children"
|
|
56
|
+
]);
|
|
57
|
+
const resolved = toElevation(elevation);
|
|
58
|
+
return /*#__PURE__*/ _jsx(MuiPaper, _extends({
|
|
59
|
+
ref: ref,
|
|
60
|
+
// `outlined` has no shadow; passing an elevation alongside it is
|
|
61
|
+
// silently ignored by MUI, so don't pretend otherwise.
|
|
62
|
+
variant: variant === 'outlined' ? 'outlined' : 'elevation',
|
|
63
|
+
elevation: variant === 'outlined' ? undefined : resolved
|
|
64
|
+
}, applySemanticElement(rest), {
|
|
65
|
+
children: children
|
|
66
|
+
}));
|
|
67
|
+
});
|
|
68
|
+
PaperElement.displayName = 'AglynPaper';
|
|
69
|
+
/** Elevation only means anything on the (default) `elevation` variant. */ const ELEVATION_ONLY = {
|
|
70
|
+
when: 'variant',
|
|
71
|
+
is: 'outlined',
|
|
72
|
+
notMatch: true
|
|
73
|
+
};
|
|
74
|
+
export const schema = {
|
|
75
|
+
$id: ID,
|
|
76
|
+
pluginId: BUNDLE_ID,
|
|
77
|
+
displayName: 'Paper',
|
|
78
|
+
description: 'Themed surface with a shadow or an outline.',
|
|
79
|
+
category: Aglyn.ComponentCategory.SURFACE,
|
|
80
|
+
icon: {
|
|
81
|
+
path: mdiFileDocumentOutline.path,
|
|
82
|
+
sx: {
|
|
83
|
+
color: '#2196f3'
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
attributes: [
|
|
87
|
+
semanticElementAttribute('this surface'),
|
|
88
|
+
semanticElementLabelAttribute(),
|
|
89
|
+
{
|
|
90
|
+
name: 'variant',
|
|
91
|
+
label: 'Variant',
|
|
92
|
+
description: 'Elevation raises the surface with a shadow; outlined draws a ' + '1px border instead and has no shadow.',
|
|
93
|
+
component: Aglyn.FieldComponentType.SELECT,
|
|
94
|
+
// A real sentinel rather than a deletion (AGL-1451): unlike Container,
|
|
95
|
+
// "elevation" is not a second name for something else in the list —
|
|
96
|
+
// it is MUI's own variant value, the other half of a two-way choice,
|
|
97
|
+
// and the one the Elevation control below is conditioned on. It was
|
|
98
|
+
// spelled `''`, which the attributes form strips on change (AGL-1191),
|
|
99
|
+
// so picking it reverted the field on save.
|
|
100
|
+
options: [
|
|
101
|
+
{
|
|
102
|
+
value: 'elevation',
|
|
103
|
+
label: 'Elevation (default)'
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
value: 'outlined',
|
|
107
|
+
label: 'Outlined'
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'elevation',
|
|
113
|
+
label: 'Elevation',
|
|
114
|
+
description: 'Shadow depth from 0 (flat) to 24. In dark mode MUI also lightens ' + 'the background as this rises.',
|
|
115
|
+
component: Aglyn.FieldComponentType.TEXT_FIELD,
|
|
116
|
+
type: 'number',
|
|
117
|
+
// Offering a shadow control on the outlined variant would be a
|
|
118
|
+
// control that silently does nothing.
|
|
119
|
+
condition: ELEVATION_ONLY
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'square',
|
|
123
|
+
label: 'Square corners?',
|
|
124
|
+
description: 'If true, the corners are not rounded.',
|
|
125
|
+
component: Aglyn.FieldComponentType.SWITCH
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
};
|
|
129
|
+
export const presets = [
|
|
130
|
+
{
|
|
131
|
+
$id: generatePresetId(ID),
|
|
132
|
+
type: Aglyn.NodeType.PRESET,
|
|
133
|
+
displayName: 'Paper',
|
|
134
|
+
pluginId: BUNDLE_ID,
|
|
135
|
+
description: 'Raised surface for panels and callouts',
|
|
136
|
+
category: Aglyn.ComponentCategory.SURFACE,
|
|
137
|
+
icon: schema.icon,
|
|
138
|
+
data: {
|
|
139
|
+
$id: null,
|
|
140
|
+
componentId: ID,
|
|
141
|
+
pluginId: BUNDLE_ID,
|
|
142
|
+
props: {
|
|
143
|
+
elevation: 1
|
|
144
|
+
},
|
|
145
|
+
// Node-level sx (AGL-1346) — the record the Styles panel edits.
|
|
146
|
+
sx: {
|
|
147
|
+
paddingTop: 2,
|
|
148
|
+
paddingRight: 2,
|
|
149
|
+
paddingBottom: 2,
|
|
150
|
+
paddingLeft: 2
|
|
151
|
+
},
|
|
152
|
+
nodes: [
|
|
153
|
+
{
|
|
154
|
+
$id: null,
|
|
155
|
+
componentId: 'muiTypography',
|
|
156
|
+
pluginId: BUNDLE_ID,
|
|
157
|
+
props: {
|
|
158
|
+
variant: 'body1',
|
|
159
|
+
children: 'Paper is a themed surface. Drop anything inside it.'
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
$id: generatePresetId(ID, 'outlined'),
|
|
167
|
+
type: Aglyn.NodeType.PRESET,
|
|
168
|
+
displayName: 'Paper Outlined',
|
|
169
|
+
pluginId: BUNDLE_ID,
|
|
170
|
+
description: 'Flat surface with a border instead of a shadow',
|
|
171
|
+
category: Aglyn.ComponentCategory.SURFACE,
|
|
172
|
+
icon: schema.icon,
|
|
173
|
+
data: {
|
|
174
|
+
$id: null,
|
|
175
|
+
componentId: ID,
|
|
176
|
+
pluginId: BUNDLE_ID,
|
|
177
|
+
props: {
|
|
178
|
+
variant: 'outlined'
|
|
179
|
+
},
|
|
180
|
+
sx: {
|
|
181
|
+
paddingTop: 2,
|
|
182
|
+
paddingRight: 2,
|
|
183
|
+
paddingBottom: 2,
|
|
184
|
+
paddingLeft: 2
|
|
185
|
+
},
|
|
186
|
+
nodes: [
|
|
187
|
+
{
|
|
188
|
+
$id: null,
|
|
189
|
+
componentId: 'muiTypography',
|
|
190
|
+
pluginId: BUNDLE_ID,
|
|
191
|
+
props: {
|
|
192
|
+
variant: 'body1',
|
|
193
|
+
children: 'Outlined paper has a border and no shadow.'
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
];
|
|
200
|
+
export default PaperElement;
|
|
201
|
+
|
|
202
|
+
//# sourceMappingURL=paper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/paper.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 { mdiFileDocumentOutline } from '@aglyn/shared-data-mdi'\nimport MuiPaper from '@mui/material/Paper'\nimport { forwardRef, type ReactNode } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport {\n applySemanticElement,\n semanticElementAttribute,\n semanticElementLabelAttribute,\n} from '../utils/element-picker'\nimport { dropClearedProps } from '../utils/drop-cleared-props'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'muiPaper'\n\nexport interface PaperElementProps {\n /** Shadow depth, 0–24. Ignored by MUI when `variant` is `outlined`. */\n elevation?: number | string\n variant?: 'elevation' | 'outlined'\n /** If true, corners are not rounded. */\n square?: boolean\n children?: ReactNode\n}\n\n/** MUI's shadow scale tops out at 24; anything above renders unshadowed. */\nexport const MAX_ELEVATION = 24\n\n/**\n * Coerces a persisted elevation to the number MUI's shadow lookup needs.\n *\n * Number-typed attribute fields round-trip through the document as\n * strings often enough that this can't be assumed away: `elevation=\"3\"`\n * indexes MUI's shadow array with a string and yields `undefined`, i.e. a\n * paper the author gave a shadow renders completely flat.\n */\nexport function toElevation(value: unknown): number | undefined {\n if (value === '' || value == null) return undefined\n const parsed = Number(value)\n if (!Number.isFinite(parsed)) return undefined\n return Math.min(Math.max(Math.round(parsed), 0), MAX_ELEVATION)\n}\n\n/**\n * Surface primitive (https://mui.com/material-ui/react-paper/): the\n * themed background + shadow that Card, Accordion and Drawer are all\n * built on, exposed on its own for panels and callouts.\n */\nconst PaperElement = forwardRef<HTMLDivElement, PaperElementProps>(\n (rawProps, ref) => {\n // Cleared props dropped before anything reads them (AGL-1451): `square`\n // and everything else in `rest` spread straight into MUI, where a `null`\n // from the field's ✕ (AGL-1226) is a value rather than an absence.\n const props = dropClearedProps(rawProps)\n const { elevation, variant, children, ...rest } = props\n const resolved = toElevation(elevation)\n return (\n <MuiPaper\n ref={ref}\n // `outlined` has no shadow; passing an elevation alongside it is\n // silently ignored by MUI, so don't pretend otherwise.\n variant={variant === 'outlined' ? 'outlined' : 'elevation'}\n elevation={variant === 'outlined' ? undefined : resolved}\n {...applySemanticElement(rest as Record<string, unknown>)}\n >\n {children}\n </MuiPaper>\n )\n },\n)\nPaperElement.displayName = 'AglynPaper'\n\n/** Elevation only means anything on the (default) `elevation` variant. */\nconst ELEVATION_ONLY = { when: 'variant', is: 'outlined', notMatch: true }\n\nexport const schema: Aglyn.ComponentSchema<PaperElementProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Paper',\n description: 'Themed surface with a shadow or an outline.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: { path: mdiFileDocumentOutline.path, sx: { color: '#2196f3' } },\n attributes: [\n semanticElementAttribute('this surface'),\n semanticElementLabelAttribute(),\n {\n name: 'variant',\n label: 'Variant',\n description:\n 'Elevation raises the surface with a shadow; outlined draws a ' +\n '1px border instead and has no shadow.',\n component: Aglyn.FieldComponentType.SELECT,\n // A real sentinel rather than a deletion (AGL-1451): unlike Container,\n // \"elevation\" is not a second name for something else in the list —\n // it is MUI's own variant value, the other half of a two-way choice,\n // and the one the Elevation control below is conditioned on. It was\n // spelled `''`, which the attributes form strips on change (AGL-1191),\n // so picking it reverted the field on save.\n options: [\n { value: 'elevation', label: 'Elevation (default)' },\n { value: 'outlined', label: 'Outlined' },\n ],\n },\n {\n name: 'elevation',\n label: 'Elevation',\n description:\n 'Shadow depth from 0 (flat) to 24. In dark mode MUI also lightens ' +\n 'the background as this rises.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n type: 'number',\n // Offering a shadow control on the outlined variant would be a\n // control that silently does nothing.\n condition: ELEVATION_ONLY,\n },\n {\n name: 'square',\n label: 'Square corners?',\n description: 'If true, the corners are not rounded.',\n component: Aglyn.FieldComponentType.SWITCH,\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Paper',\n pluginId: BUNDLE_ID,\n description: 'Raised surface for panels and callouts',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: schema.icon,\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { elevation: 1 },\n // Node-level sx (AGL-1346) — the record the Styles panel edits.\n sx: { paddingTop: 2, paddingRight: 2, paddingBottom: 2, paddingLeft: 2 },\n nodes: [\n {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: {\n variant: 'body1',\n children: 'Paper is a themed surface. Drop anything inside it.',\n },\n },\n ],\n },\n },\n {\n $id: generatePresetId(ID, 'outlined'),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Paper Outlined',\n pluginId: BUNDLE_ID,\n description: 'Flat surface with a border instead of a shadow',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: schema.icon,\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: { variant: 'outlined' },\n sx: { paddingTop: 2, paddingRight: 2, paddingBottom: 2, paddingLeft: 2 },\n nodes: [\n {\n $id: null,\n componentId: 'muiTypography',\n pluginId: BUNDLE_ID,\n props: {\n variant: 'body1',\n children: 'Outlined paper has a border and no shadow.',\n },\n },\n ],\n },\n },\n]\n\nexport default PaperElement\n"],"names":["Aglyn","mdiFileDocumentOutline","MuiPaper","forwardRef","BUNDLE_ID","applySemanticElement","semanticElementAttribute","semanticElementLabelAttribute","dropClearedProps","generatePresetId","ID","MAX_ELEVATION","toElevation","value","undefined","parsed","Number","isFinite","Math","min","max","round","PaperElement","rawProps","ref","props","elevation","variant","children","rest","resolved","displayName","ELEVATION_ONLY","when","is","notMatch","schema","$id","pluginId","description","category","ComponentCategory","SURFACE","icon","path","sx","color","attributes","name","label","component","FieldComponentType","SELECT","options","TEXT_FIELD","type","condition","SWITCH","presets","NodeType","PRESET","data","componentId","paddingTop","paddingRight","paddingBottom","paddingLeft","nodes"],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as Aglyn from '@aglyn/aglyn';
|
|
2
|
+
import type { SxProps } from '@mui/material/styles';
|
|
3
|
+
/**
|
|
4
|
+
* Host-side sandboxed plugin runtime (AGL-45), per AGL-43 §2. Renders an
|
|
5
|
+
* installed executable plugin inside a cross-origin sandboxed iframe and
|
|
6
|
+
* talks to it only through the capability-scoped postMessage bridge — the
|
|
7
|
+
* plugin never runs in the console/tenant realm, so it has no ambient
|
|
8
|
+
* access to host Firestore, auth, storage, or the DOM. Renders a safe
|
|
9
|
+
* placeholder (never the plugin) whenever it can't run safely: no plugin
|
|
10
|
+
* origin configured, the origin isn't cross-origin to this app, or the
|
|
11
|
+
* version is revoked (kill switch).
|
|
12
|
+
*
|
|
13
|
+
* SANDBOX NOTE: `allow-scripts allow-same-origin` is correct HERE and only
|
|
14
|
+
* here because `src` is a DEDICATED cross-origin (`pluginOrigin`). For a
|
|
15
|
+
* cross-origin frame, `allow-same-origin` grants the frame ITS OWN
|
|
16
|
+
* (plugin) origin's privileges — not the parent's — so it stays walled off
|
|
17
|
+
* from the app while still being able to load its own bundle and honor the
|
|
18
|
+
* plugin-origin CSP's `connect-src`. The combination is only unsafe for a
|
|
19
|
+
* SAME-origin frame; `isCrossOrigin` below refuses that case.
|
|
20
|
+
*
|
|
21
|
+
* SERVER RENDER: the origin comparison needs `window.location.origin`, so a
|
|
22
|
+
* server render cannot make it and neither render that has to agree with the
|
|
23
|
+
* server makes it. Both emit the frame with no `src` — the plugin URL is
|
|
24
|
+
* attached on the render after mount, once the origin has actually been
|
|
25
|
+
* checked. That keeps the server HTML and the first client render identical
|
|
26
|
+
* whatever `pluginOrigin` is set to, and it keeps an unverified origin out of
|
|
27
|
+
* the markup a browser would start loading before any of this code runs.
|
|
28
|
+
*/
|
|
29
|
+
export interface PluginFrameProps {
|
|
30
|
+
/** Dedicated plugin origin, e.g. `https://plugins.aglyn.app`. */
|
|
31
|
+
pluginOrigin?: string;
|
|
32
|
+
/** Host id, for scoping host-mediated fetch (AGL-191). */
|
|
33
|
+
hostId?: string;
|
|
34
|
+
listingId: string;
|
|
35
|
+
/**
|
|
36
|
+
* Which DECLARED element of the plugin to render (AGL-1031).
|
|
37
|
+
*
|
|
38
|
+
* A FRAME prop, never a member of `pluginProps`: those are filtered against
|
|
39
|
+
* the manifest allowlist, so an element id routed through them would be
|
|
40
|
+
* dropped for every plugin that had not thought to declare `elementId` as a
|
|
41
|
+
* settable prop — silently, and only for declared elements. It is host
|
|
42
|
+
* metadata like `version` and `sha256`, and travels with them.
|
|
43
|
+
*/
|
|
44
|
+
elementId?: string;
|
|
45
|
+
version: string;
|
|
46
|
+
sha256: string;
|
|
47
|
+
/** Manifest capabilities (event allowlist, declared size). */
|
|
48
|
+
capabilities?: Aglyn.PluginCapabilities;
|
|
49
|
+
/** Props to pass in; filtered to the manifest allowlist before sending. */
|
|
50
|
+
pluginProps?: Record<string, unknown>;
|
|
51
|
+
/** True when the pinned version is revoked (from the revocations doc). */
|
|
52
|
+
revoked?: boolean;
|
|
53
|
+
scheme?: 'light' | 'dark';
|
|
54
|
+
title?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Authored node styles, handed over by the renderer rather than typed
|
|
57
|
+
* into an attribute — recomposed below so the Styles panel's values are
|
|
58
|
+
* merged rather than replaced (AGL-1284).
|
|
59
|
+
*/
|
|
60
|
+
sx?: SxProps;
|
|
61
|
+
}
|
|
62
|
+
declare const PluginFrame: import("react").ForwardRefExoticComponent<PluginFrameProps & import("react").RefAttributes<HTMLIFrameElement>>;
|
|
63
|
+
export { PluginFrame };
|
|
64
|
+
export default PluginFrame;
|
|
@@ -0,0 +1,335 @@
|
|
|
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
|
+
*/ 'use client';
|
|
17
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
18
|
+
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
19
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
20
|
+
import * as Aglyn from "@aglyn/aglyn";
|
|
21
|
+
import Box from "@mui/material/Box";
|
|
22
|
+
import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
|
|
23
|
+
/**
|
|
24
|
+
* Does `pluginOrigin` resolve to an origin other than the app's own?
|
|
25
|
+
*
|
|
26
|
+
* True is the only answer that lets the frame render: the sandbox pair in
|
|
27
|
+
* the SANDBOX NOTE is containment exactly while the frame's origin differs
|
|
28
|
+
* from the page's, and is a no-op when it doesn't.
|
|
29
|
+
*
|
|
30
|
+
* Browser-only, and unavoidably so. The app's origin is
|
|
31
|
+
* `window.location.origin` and no constant stands in for it — a site answers
|
|
32
|
+
* on both its platform address and any custom domain attached to it, and the
|
|
33
|
+
* console, a preview, a self-host container and a dev server each run this
|
|
34
|
+
* under a different one. Callers ask only once mounted; the `catch` refuses
|
|
35
|
+
* an unparsable origin, and refuses a call made without a `window` for the
|
|
36
|
+
* same reason — an origin nothing compared is not an origin anything cleared.
|
|
37
|
+
*/ function isCrossOrigin(pluginOrigin) {
|
|
38
|
+
try {
|
|
39
|
+
return new URL(pluginOrigin).origin !== window.location.origin;
|
|
40
|
+
} catch (unused) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const Placeholder = /*#__PURE__*/ forwardRef(({ message, height }, ref)=>/*#__PURE__*/ _jsx(Box, {
|
|
45
|
+
ref: ref,
|
|
46
|
+
sx: {
|
|
47
|
+
minHeight: height != null ? height : 120,
|
|
48
|
+
display: 'flex',
|
|
49
|
+
alignItems: 'center',
|
|
50
|
+
justifyContent: 'center',
|
|
51
|
+
border: '1px dashed',
|
|
52
|
+
borderColor: 'divider',
|
|
53
|
+
borderRadius: 1,
|
|
54
|
+
color: 'text.secondary',
|
|
55
|
+
fontSize: 13,
|
|
56
|
+
fontFamily: 'system-ui, sans-serif',
|
|
57
|
+
p: 2,
|
|
58
|
+
textAlign: 'center'
|
|
59
|
+
},
|
|
60
|
+
children: message
|
|
61
|
+
}));
|
|
62
|
+
Placeholder.displayName = 'PluginFramePlaceholder';
|
|
63
|
+
const PluginFrame = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
64
|
+
var _ref;
|
|
65
|
+
var _capabilities_size;
|
|
66
|
+
const { pluginOrigin, hostId, listingId, elementId, version, sha256, capabilities, pluginProps, revoked, scheme, title } = props, rest = _object_without_properties_loose(props, [
|
|
67
|
+
"pluginOrigin",
|
|
68
|
+
"hostId",
|
|
69
|
+
"listingId",
|
|
70
|
+
"elementId",
|
|
71
|
+
"version",
|
|
72
|
+
"sha256",
|
|
73
|
+
"capabilities",
|
|
74
|
+
"pluginProps",
|
|
75
|
+
"revoked",
|
|
76
|
+
"scheme",
|
|
77
|
+
"title"
|
|
78
|
+
]);
|
|
79
|
+
// Node styles ride the renderer-merged sx; recompose (stack.ts pattern).
|
|
80
|
+
const nodeSx = Array.isArray(props['sx']) ? props['sx'] : [
|
|
81
|
+
props['sx']
|
|
82
|
+
];
|
|
83
|
+
const frameRef = useRef(null);
|
|
84
|
+
const [state, setState] = useState('loading');
|
|
85
|
+
const [height, setHeight] = useState((_ref = capabilities == null ? void 0 : (_capabilities_size = capabilities.size) == null ? void 0 : _capabilities_size.height) != null ? _ref : 120);
|
|
86
|
+
/**
|
|
87
|
+
* Whether this render is running in a browser, where `isCrossOrigin` can
|
|
88
|
+
* be answered.
|
|
89
|
+
*
|
|
90
|
+
* Every other input here is a prop or a build-time env and so reads the
|
|
91
|
+
* same on both sides; the origin comparison is the one that doesn't, and
|
|
92
|
+
* a render that branches on it disagrees with the server for any
|
|
93
|
+
* `pluginOrigin` that is same-origin to the app. Deferring it to the
|
|
94
|
+
* render after mount costs nothing visible: the frame is hidden until the
|
|
95
|
+
* guest posts `ready`, and the listener that hears `ready` is registered
|
|
96
|
+
* in an effect, so no plugin has ever painted before this point either.
|
|
97
|
+
*/ const [mounted, setMounted] = useState(false);
|
|
98
|
+
useEffect(()=>setMounted(true), []);
|
|
99
|
+
const allowedEvents = capabilities == null ? void 0 : capabilities.events;
|
|
100
|
+
const allowedProps = capabilities == null ? void 0 : capabilities.props;
|
|
101
|
+
const filteredProps = useMemo(()=>Aglyn.filterPluginProps(pluginProps, allowedProps), [
|
|
102
|
+
pluginProps,
|
|
103
|
+
allowedProps
|
|
104
|
+
]);
|
|
105
|
+
// Stable dependency for the props-update effect.
|
|
106
|
+
const filteredPropsKey = useMemo(()=>JSON.stringify(filteredProps), [
|
|
107
|
+
filteredProps
|
|
108
|
+
]);
|
|
109
|
+
/**
|
|
110
|
+
* Three answers, not two: `null` while the check is still unanswerable,
|
|
111
|
+
* `false` once the browser has refused the origin. Collapsing them would
|
|
112
|
+
* put the refusal placeholder in the server HTML of every correctly
|
|
113
|
+
* configured site, which is the same mismatch pointing the other way.
|
|
114
|
+
*/ const crossOrigin = mounted && pluginOrigin ? isCrossOrigin(pluginOrigin) : null;
|
|
115
|
+
const originUsable = !revoked && crossOrigin === true;
|
|
116
|
+
// Undefined, never `''`: an empty `src` is still an `src`, and the frame
|
|
117
|
+
// must carry no address at all until the origin behind it has cleared.
|
|
118
|
+
const src = useMemo(()=>{
|
|
119
|
+
if (!originUsable) return undefined;
|
|
120
|
+
const url = new URL('/load', String(pluginOrigin));
|
|
121
|
+
url.searchParams.set('listing', listingId);
|
|
122
|
+
url.searchParams.set('v', version);
|
|
123
|
+
url.searchParams.set('sha', sha256);
|
|
124
|
+
if (elementId) url.searchParams.set('element', elementId);
|
|
125
|
+
// The framing host (AGL-884): the origin resolves this to the site's
|
|
126
|
+
// VERIFIED custom domain and widens frame-ancestors to exactly that —
|
|
127
|
+
// never to caller-supplied origins.
|
|
128
|
+
if (hostId) url.searchParams.set('host', hostId);
|
|
129
|
+
return url.toString();
|
|
130
|
+
}, [
|
|
131
|
+
originUsable,
|
|
132
|
+
pluginOrigin,
|
|
133
|
+
hostId,
|
|
134
|
+
listingId,
|
|
135
|
+
elementId,
|
|
136
|
+
version,
|
|
137
|
+
sha256
|
|
138
|
+
]);
|
|
139
|
+
// Bridge: validate every inbound message by origin + source + schema,
|
|
140
|
+
// then honor resize / surface events / flag errors. Never eval or graft
|
|
141
|
+
// anything from the frame — only sized output and named events.
|
|
142
|
+
useEffect(()=>{
|
|
143
|
+
if (!originUsable) return;
|
|
144
|
+
const expectedOrigin = new URL(String(pluginOrigin)).origin;
|
|
145
|
+
const handleMessage = (event)=>{
|
|
146
|
+
var _frameRef_current;
|
|
147
|
+
if (event.origin !== expectedOrigin) return;
|
|
148
|
+
if (event.source !== ((_frameRef_current = frameRef.current) == null ? void 0 : _frameRef_current.contentWindow)) return;
|
|
149
|
+
const message = Aglyn.parseGuestMessage(event.data, allowedEvents);
|
|
150
|
+
if (!message) return;
|
|
151
|
+
switch(message.type){
|
|
152
|
+
case 'ready':
|
|
153
|
+
var _frameRef_current_contentWindow, _frameRef_current1;
|
|
154
|
+
setState('ready');
|
|
155
|
+
(_frameRef_current1 = frameRef.current) == null ? void 0 : (_frameRef_current_contentWindow = _frameRef_current1.contentWindow) == null ? void 0 : _frameRef_current_contentWindow.postMessage(_extends({
|
|
156
|
+
type: 'init',
|
|
157
|
+
v: Aglyn.PLUGIN_BRIDGE_VERSION,
|
|
158
|
+
props: filteredProps
|
|
159
|
+
}, scheme ? {
|
|
160
|
+
scheme
|
|
161
|
+
} : {}), expectedOrigin);
|
|
162
|
+
break;
|
|
163
|
+
case 'resize':
|
|
164
|
+
setHeight(Math.max(24, message.height));
|
|
165
|
+
break;
|
|
166
|
+
case 'event':
|
|
167
|
+
var _Aglyn_emitter_emit, _Aglyn_emitter;
|
|
168
|
+
(_Aglyn_emitter = Aglyn.emitter) == null ? void 0 : (_Aglyn_emitter_emit = _Aglyn_emitter.emit) == null ? void 0 : _Aglyn_emitter_emit.call(_Aglyn_emitter, `plugin:${listingId}:${message.name}`, {
|
|
169
|
+
listingId,
|
|
170
|
+
name: message.name,
|
|
171
|
+
payload: message.payload
|
|
172
|
+
});
|
|
173
|
+
break;
|
|
174
|
+
case 'fetch-request':
|
|
175
|
+
{
|
|
176
|
+
// Host-mediated fetch (AGL-191): proxy through the host API,
|
|
177
|
+
// which re-checks the manifest allowlist server-side. Reply on
|
|
178
|
+
// the same id; a disallowed/failed call resolves ok:false.
|
|
179
|
+
const requestId = message.id;
|
|
180
|
+
const respond = (payload)=>{
|
|
181
|
+
var _frameRef_current_contentWindow, _frameRef_current;
|
|
182
|
+
return (_frameRef_current = frameRef.current) == null ? void 0 : (_frameRef_current_contentWindow = _frameRef_current.contentWindow) == null ? void 0 : _frameRef_current_contentWindow.postMessage(_extends({
|
|
183
|
+
type: 'fetch-response',
|
|
184
|
+
v: Aglyn.PLUGIN_BRIDGE_VERSION,
|
|
185
|
+
id: requestId
|
|
186
|
+
}, payload), expectedOrigin);
|
|
187
|
+
};
|
|
188
|
+
if (!hostId) {
|
|
189
|
+
respond({
|
|
190
|
+
ok: false,
|
|
191
|
+
status: 0,
|
|
192
|
+
error: 'No host context'
|
|
193
|
+
});
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
void fetch('/api/plugins/fetch', {
|
|
197
|
+
method: 'POST',
|
|
198
|
+
headers: {
|
|
199
|
+
'Content-Type': 'application/json'
|
|
200
|
+
},
|
|
201
|
+
body: JSON.stringify({
|
|
202
|
+
hostId,
|
|
203
|
+
listingId,
|
|
204
|
+
url: message.url,
|
|
205
|
+
method: message.method,
|
|
206
|
+
body: message.body
|
|
207
|
+
})
|
|
208
|
+
}).then((response)=>response.json()).then((payload)=>{
|
|
209
|
+
var _ref;
|
|
210
|
+
return respond({
|
|
211
|
+
ok: Boolean(payload == null ? void 0 : payload.ok),
|
|
212
|
+
status: Number((_ref = payload == null ? void 0 : payload.status) != null ? _ref : 0),
|
|
213
|
+
body: payload == null ? void 0 : payload.body,
|
|
214
|
+
error: payload == null ? void 0 : payload.error
|
|
215
|
+
});
|
|
216
|
+
}).catch(()=>respond({
|
|
217
|
+
ok: false,
|
|
218
|
+
status: 0,
|
|
219
|
+
error: 'Fetch failed'
|
|
220
|
+
}));
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
case 'error':
|
|
224
|
+
setState('error');
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
window.addEventListener('message', handleMessage);
|
|
229
|
+
return ()=>window.removeEventListener('message', handleMessage);
|
|
230
|
+
// filteredProps is re-sent by the effect below; init uses the latest.
|
|
231
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
232
|
+
}, [
|
|
233
|
+
originUsable,
|
|
234
|
+
pluginOrigin,
|
|
235
|
+
hostId,
|
|
236
|
+
listingId,
|
|
237
|
+
allowedEvents,
|
|
238
|
+
scheme,
|
|
239
|
+
src
|
|
240
|
+
]);
|
|
241
|
+
// Push prop updates to an already-ready frame.
|
|
242
|
+
useEffect(()=>{
|
|
243
|
+
var _frameRef_current_contentWindow, _frameRef_current;
|
|
244
|
+
if (state !== 'ready' || !originUsable) return;
|
|
245
|
+
const expectedOrigin = new URL(String(pluginOrigin)).origin;
|
|
246
|
+
(_frameRef_current = frameRef.current) == null ? void 0 : (_frameRef_current_contentWindow = _frameRef_current.contentWindow) == null ? void 0 : _frameRef_current_contentWindow.postMessage({
|
|
247
|
+
type: 'props',
|
|
248
|
+
v: Aglyn.PLUGIN_BRIDGE_VERSION,
|
|
249
|
+
props: filteredProps
|
|
250
|
+
}, expectedOrigin);
|
|
251
|
+
// filteredPropsKey captures the serialized props identity.
|
|
252
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
253
|
+
}, [
|
|
254
|
+
filteredPropsKey,
|
|
255
|
+
state,
|
|
256
|
+
originUsable,
|
|
257
|
+
pluginOrigin
|
|
258
|
+
]);
|
|
259
|
+
if (revoked) {
|
|
260
|
+
return /*#__PURE__*/ _jsx(Placeholder, {
|
|
261
|
+
message: "This plugin has been disabled by the platform."
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
if (!pluginOrigin) {
|
|
265
|
+
return /*#__PURE__*/ _jsx(Placeholder, {
|
|
266
|
+
message: "Plugins are not enabled on this deployment."
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
// A refusal the browser actually made. `null` — not yet checked — falls
|
|
270
|
+
// through to the frame below, which carries no `src` until it clears.
|
|
271
|
+
if (crossOrigin === false) {
|
|
272
|
+
return /*#__PURE__*/ _jsx(Placeholder, {
|
|
273
|
+
message: "Plugin cannot be loaded safely here."
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
return /*#__PURE__*/ _jsx(Box, {
|
|
277
|
+
sx: {
|
|
278
|
+
position: 'relative',
|
|
279
|
+
width: '100%'
|
|
280
|
+
},
|
|
281
|
+
children: state === 'error' ? /*#__PURE__*/ _jsx(Placeholder, {
|
|
282
|
+
message: "This plugin failed to load.",
|
|
283
|
+
height: height
|
|
284
|
+
}) : /*#__PURE__*/ _jsx(Box, _extends({
|
|
285
|
+
component: "iframe",
|
|
286
|
+
ref: (node)=>{
|
|
287
|
+
frameRef.current = node;
|
|
288
|
+
if (typeof ref === 'function') ref(node);
|
|
289
|
+
else if (ref) ref.current = node;
|
|
290
|
+
},
|
|
291
|
+
src: src,
|
|
292
|
+
title: title != null ? title : 'Plugin',
|
|
293
|
+
loading: "lazy",
|
|
294
|
+
onError: ()=>setState('error')
|
|
295
|
+
}, rest, {
|
|
296
|
+
// AFTER the spread, deliberately (AGL-2484). See SANDBOX NOTE —
|
|
297
|
+
// this pair is safe only because `src` is a dedicated
|
|
298
|
+
// cross-origin, and JSX resolves a duplicate attribute by taking
|
|
299
|
+
// the LAST one, so listing them before `{...rest}` made the
|
|
300
|
+
// frame's only containment overridable by whatever a caller
|
|
301
|
+
// happened to spread in. No caller passes either today and
|
|
302
|
+
// `PluginFrameProps` declares neither; the props here do arrive
|
|
303
|
+
// from renderers that forward author-controlled bags, which is
|
|
304
|
+
// how "no caller does this" stops being true.
|
|
305
|
+
sandbox: "allow-scripts allow-same-origin",
|
|
306
|
+
referrerPolicy: "no-referrer",
|
|
307
|
+
// MERGE, never replace (AGL-1284). The spread in the old literal
|
|
308
|
+
// LOOKED like a merge and merged nothing — it folded in a local
|
|
309
|
+
// conditional, not the node's `sx`, so every style an author set
|
|
310
|
+
// on a plugin element was discarded. Order matters: the frame's
|
|
311
|
+
// own box goes first so the author can override it, and the
|
|
312
|
+
// load-time hide goes LAST so an authored `visibility` can never
|
|
313
|
+
// reveal a frame that has not finished loading.
|
|
314
|
+
sx: [
|
|
315
|
+
{
|
|
316
|
+
width: '100%',
|
|
317
|
+
height,
|
|
318
|
+
border: 0,
|
|
319
|
+
display: 'block'
|
|
320
|
+
},
|
|
321
|
+
...nodeSx,
|
|
322
|
+
...state === 'loading' ? [
|
|
323
|
+
{
|
|
324
|
+
visibility: 'hidden'
|
|
325
|
+
}
|
|
326
|
+
] : []
|
|
327
|
+
]
|
|
328
|
+
}))
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
PluginFrame.displayName = 'AglynPluginFrame';
|
|
332
|
+
export { PluginFrame };
|
|
333
|
+
export default PluginFrame;
|
|
334
|
+
|
|
335
|
+
//# sourceMappingURL=plugin-frame.js.map
|