@aglyn/plugins-mui 1.0.0-beta.143

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +22 -0
  3. package/package.json +47 -0
  4. package/src/index.d.ts +21 -0
  5. package/src/index.js +22 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/components/accordion.d.ts +96 -0
  8. package/src/lib/components/accordion.js +392 -0
  9. package/src/lib/components/accordion.js.map +1 -0
  10. package/src/lib/components/app-bar.d.ts +32 -0
  11. package/src/lib/components/app-bar.js +86 -0
  12. package/src/lib/components/app-bar.js.map +1 -0
  13. package/src/lib/components/blocks.d.ts +46 -0
  14. package/src/lib/components/blocks.js +1007 -0
  15. package/src/lib/components/blocks.js.map +1 -0
  16. package/src/lib/components/box.d.ts +47 -0
  17. package/src/lib/components/box.js +125 -0
  18. package/src/lib/components/box.js.map +1 -0
  19. package/src/lib/components/breadcrumbs.d.ts +44 -0
  20. package/src/lib/components/breadcrumbs.js +172 -0
  21. package/src/lib/components/breadcrumbs.js.map +1 -0
  22. package/src/lib/components/button.d.ts +89 -0
  23. package/src/lib/components/button.js +272 -0
  24. package/src/lib/components/button.js.map +1 -0
  25. package/src/lib/components/card.d.ts +61 -0
  26. package/src/lib/components/card.js +336 -0
  27. package/src/lib/components/card.js.map +1 -0
  28. package/src/lib/components/collection.d.ts +514 -0
  29. package/src/lib/components/collection.js +2999 -0
  30. package/src/lib/components/collection.js.map +1 -0
  31. package/src/lib/components/container.d.ts +42 -0
  32. package/src/lib/components/container.js +169 -0
  33. package/src/lib/components/container.js.map +1 -0
  34. package/src/lib/components/custom-html.d.ts +78 -0
  35. package/src/lib/components/custom-html.js +219 -0
  36. package/src/lib/components/custom-html.js.map +1 -0
  37. package/src/lib/components/data-table.d.ts +57 -0
  38. package/src/lib/components/data-table.js +216 -0
  39. package/src/lib/components/data-table.js.map +1 -0
  40. package/src/lib/components/document-root.d.ts +77 -0
  41. package/src/lib/components/document-root.js +118 -0
  42. package/src/lib/components/document-root.js.map +1 -0
  43. package/src/lib/components/drawer.d.ts +93 -0
  44. package/src/lib/components/drawer.js +566 -0
  45. package/src/lib/components/drawer.js.map +1 -0
  46. package/src/lib/components/function-controls.d.ts +66 -0
  47. package/src/lib/components/function-controls.js +207 -0
  48. package/src/lib/components/function-controls.js.map +1 -0
  49. package/src/lib/components/function-scope.d.ts +110 -0
  50. package/src/lib/components/function-scope.js +478 -0
  51. package/src/lib/components/function-scope.js.map +1 -0
  52. package/src/lib/components/function-widget.d.ts +75 -0
  53. package/src/lib/components/function-widget.js +307 -0
  54. package/src/lib/components/function-widget.js.map +1 -0
  55. package/src/lib/components/grid.d.ts +91 -0
  56. package/src/lib/components/grid.js +322 -0
  57. package/src/lib/components/grid.js.map +1 -0
  58. package/src/lib/components/icon.d.ts +54 -0
  59. package/src/lib/components/icon.js +163 -0
  60. package/src/lib/components/icon.js.map +1 -0
  61. package/src/lib/components/image-list.d.ts +58 -0
  62. package/src/lib/components/image-list.js +336 -0
  63. package/src/lib/components/image-list.js.map +1 -0
  64. package/src/lib/components/image.d.ts +154 -0
  65. package/src/lib/components/image.js +451 -0
  66. package/src/lib/components/image.js.map +1 -0
  67. package/src/lib/components/inline-text.d.ts +74 -0
  68. package/src/lib/components/inline-text.js +407 -0
  69. package/src/lib/components/inline-text.js.map +1 -0
  70. package/src/lib/components/language-switcher.d.ts +35 -0
  71. package/src/lib/components/language-switcher.js +179 -0
  72. package/src/lib/components/language-switcher.js.map +1 -0
  73. package/src/lib/components/layout-slot.d.ts +68 -0
  74. package/src/lib/components/layout-slot.js +192 -0
  75. package/src/lib/components/layout-slot.js.map +1 -0
  76. package/src/lib/components/link-box-accessible-name.d.ts +79 -0
  77. package/src/lib/components/link-box-accessible-name.js +158 -0
  78. package/src/lib/components/link-box-accessible-name.js.map +1 -0
  79. package/src/lib/components/link-box.d.ts +66 -0
  80. package/src/lib/components/link-box.js +212 -0
  81. package/src/lib/components/link-box.js.map +1 -0
  82. package/src/lib/components/list-item-text.d.ts +22 -0
  83. package/src/lib/components/list-item-text.js +86 -0
  84. package/src/lib/components/list-item-text.js.map +1 -0
  85. package/src/lib/components/list-item.d.ts +32 -0
  86. package/src/lib/components/list-item.js +127 -0
  87. package/src/lib/components/list-item.js.map +1 -0
  88. package/src/lib/components/list.d.ts +34 -0
  89. package/src/lib/components/list.js +108 -0
  90. package/src/lib/components/list.js.map +1 -0
  91. package/src/lib/components/markdown.d.ts +129 -0
  92. package/src/lib/components/markdown.js +853 -0
  93. package/src/lib/components/markdown.js.map +1 -0
  94. package/src/lib/components/nav-menu.d.ts +68 -0
  95. package/src/lib/components/nav-menu.js +683 -0
  96. package/src/lib/components/nav-menu.js.map +1 -0
  97. package/src/lib/components/pagination.d.ts +59 -0
  98. package/src/lib/components/pagination.js +243 -0
  99. package/src/lib/components/pagination.js.map +1 -0
  100. package/src/lib/components/paper.d.ts +47 -0
  101. package/src/lib/components/paper.js +202 -0
  102. package/src/lib/components/paper.js.map +1 -0
  103. package/src/lib/components/plugin-frame.d.ts +64 -0
  104. package/src/lib/components/plugin-frame.js +335 -0
  105. package/src/lib/components/plugin-frame.js.map +1 -0
  106. package/src/lib/components/plugin.d.ts +95 -0
  107. package/src/lib/components/plugin.js +304 -0
  108. package/src/lib/components/plugin.js.map +1 -0
  109. package/src/lib/components/product.d.ts +40 -0
  110. package/src/lib/components/product.js +287 -0
  111. package/src/lib/components/product.js.map +1 -0
  112. package/src/lib/components/reusable-instance.d.ts +57 -0
  113. package/src/lib/components/reusable-instance.js +126 -0
  114. package/src/lib/components/reusable-instance.js.map +1 -0
  115. package/src/lib/components/screen-link.d.ts +85 -0
  116. package/src/lib/components/screen-link.js +324 -0
  117. package/src/lib/components/screen-link.js.map +1 -0
  118. package/src/lib/components/search-box.d.ts +41 -0
  119. package/src/lib/components/search-box.js +125 -0
  120. package/src/lib/components/search-box.js.map +1 -0
  121. package/src/lib/components/section.d.ts +56 -0
  122. package/src/lib/components/section.js +132 -0
  123. package/src/lib/components/section.js.map +1 -0
  124. package/src/lib/components/stack.d.ts +55 -0
  125. package/src/lib/components/stack.js +309 -0
  126. package/src/lib/components/stack.js.map +1 -0
  127. package/src/lib/components/tabs.d.ts +288 -0
  128. package/src/lib/components/tabs.js +788 -0
  129. package/src/lib/components/tabs.js.map +1 -0
  130. package/src/lib/components/theme-mode-switcher.d.ts +38 -0
  131. package/src/lib/components/theme-mode-switcher.js +168 -0
  132. package/src/lib/components/theme-mode-switcher.js.map +1 -0
  133. package/src/lib/components/toolbar.d.ts +37 -0
  134. package/src/lib/components/toolbar.js +117 -0
  135. package/src/lib/components/toolbar.js.map +1 -0
  136. package/src/lib/components/typography.d.ts +33 -0
  137. package/src/lib/components/typography.js +433 -0
  138. package/src/lib/components/typography.js.map +1 -0
  139. package/src/lib/components/video-lightbox-controls.d.ts +131 -0
  140. package/src/lib/components/video-lightbox-controls.js +536 -0
  141. package/src/lib/components/video-lightbox-controls.js.map +1 -0
  142. package/src/lib/components/video-lightbox.d.ts +60 -0
  143. package/src/lib/components/video-lightbox.js +247 -0
  144. package/src/lib/components/video-lightbox.js.map +1 -0
  145. package/src/lib/components/video-playback-beacon.d.ts +80 -0
  146. package/src/lib/components/video-playback-beacon.js +128 -0
  147. package/src/lib/components/video-playback-beacon.js.map +1 -0
  148. package/src/lib/components/video-player-frame.d.ts +58 -0
  149. package/src/lib/components/video-player-frame.js +51 -0
  150. package/src/lib/components/video-player-frame.js.map +1 -0
  151. package/src/lib/components/video.d.ts +220 -0
  152. package/src/lib/components/video.js +874 -0
  153. package/src/lib/components/video.js.map +1 -0
  154. package/src/lib/constants/bundle-common.d.ts +17 -0
  155. package/src/lib/constants/bundle-common.js +18 -0
  156. package/src/lib/constants/bundle-common.js.map +1 -0
  157. package/src/lib/constants/field-presets.d.ts +93 -0
  158. package/src/lib/constants/field-presets.js +248 -0
  159. package/src/lib/constants/field-presets.js.map +1 -0
  160. package/src/lib/plugin.d.ts +80 -0
  161. package/src/lib/plugin.js +599 -0
  162. package/src/lib/plugin.js.map +1 -0
  163. package/src/lib/utils/author-html-label.d.ts +73 -0
  164. package/src/lib/utils/author-html-label.js +89 -0
  165. package/src/lib/utils/author-html-label.js.map +1 -0
  166. package/src/lib/utils/drop-cleared-props.d.ts +18 -0
  167. package/src/lib/utils/drop-cleared-props.js +74 -0
  168. package/src/lib/utils/drop-cleared-props.js.map +1 -0
  169. package/src/lib/utils/element-picker.d.ts +99 -0
  170. package/src/lib/utils/element-picker.js +140 -0
  171. package/src/lib/utils/element-picker.js.map +1 -0
  172. package/src/lib/utils/generate-preset-id.d.ts +19 -0
  173. package/src/lib/utils/generate-preset-id.js +25 -0
  174. package/src/lib/utils/generate-preset-id.js.map +1 -0
  175. package/src/lib/utils/link-target-props.d.ts +53 -0
  176. package/src/lib/utils/link-target-props.js +53 -0
  177. package/src/lib/utils/link-target-props.js.map +1 -0
@@ -0,0 +1,287 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
3
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
+ /**
5
+ * @license
6
+ * Copyright 2026 Aglyn LLC
7
+ *
8
+ * Licensed under the Apache License, Version 2.0 (the "License");
9
+ * you may not use this file except in compliance with the License.
10
+ * You may obtain a copy of the License at
11
+ *
12
+ * http://www.apache.org/licenses/LICENSE-2.0
13
+ *
14
+ * Unless required by applicable law or agreed to in writing, software
15
+ * distributed under the License is distributed on an "AS IS" BASIS,
16
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ * See the License for the specific language governing permissions and
18
+ * limitations under the License.
19
+ */ import * as Aglyn from "@aglyn/aglyn";
20
+ import { buildBeginCheckoutParams, trackEventBeforeNavigation } from "@aglyn/aglyn/app-utils/analytics-events";
21
+ import { mdiCartOutline } from "@aglyn/shared-data-mdi";
22
+ import Alert from "@mui/material/Alert";
23
+ import Button from "@mui/material/Button";
24
+ import Card from "@mui/material/Card";
25
+ import CardContent from "@mui/material/CardContent";
26
+ import CardMedia from "@mui/material/CardMedia";
27
+ import TextField from "@mui/material/TextField";
28
+ import Typography from "@mui/material/Typography";
29
+ import { forwardRef, useCallback, useState } from "react";
30
+ import { BUNDLE_ID } from "../constants/bundle-common.js";
31
+ import { generatePresetId } from "../utils/generate-preset-id.js";
32
+ // Component ids are persisted in screen documents; never rename.
33
+ export const ID = 'product';
34
+ /**
35
+ * Commerce Starter product block (AGL-90): displays from its own props,
36
+ * but Buy posts {hostId, productId} to the tenant's /api/commerce/checkout,
37
+ * which prices from the server-side product doc — canvas props can't forge
38
+ * a charge. Inert without SiteContext (besigner canvas/preview).
39
+ */ const Product = /*#__PURE__*/ forwardRef((props, ref)=>{
40
+ const { productId, name, priceUsd, description, imageUrl: storedImageUrl, buyLabel, showCoupon } = props, rest = _object_without_properties_loose(props, [
41
+ "productId",
42
+ "name",
43
+ "priceUsd",
44
+ "description",
45
+ "imageUrl",
46
+ "buyLabel",
47
+ "showCoupon"
48
+ ]);
49
+ const { hostId } = Aglyn.useSite();
50
+ const siteFetch = Aglyn.useSiteFetch();
51
+ // A media-picker target, so it can hold a media reference (AGL-1215) as
52
+ // well as any URL form that predates it. `siteRelativeMediaSrc` rather
53
+ // than the bare resolver (AGL-1726): one of those older forms is an
54
+ // absolute `https://{subdomain}.aglyn.app/api/media/cdn/…`, which on a
55
+ // white-label storefront names our platform in the customer's page source
56
+ // and fetches cross-origin from their own domain.
57
+ const imageUrl = Aglyn.siteRelativeMediaSrc(storedImageUrl, {
58
+ hostId
59
+ });
60
+ const [status, setStatus] = useState('idle');
61
+ const [coupon, setCoupon] = useState('');
62
+ const [message, setMessage] = useState('');
63
+ const soldOut = message === 'Sold out';
64
+ const handleBuy = useCallback(async ()=>{
65
+ if (!hostId || !productId || status === 'sending') return;
66
+ setStatus('sending');
67
+ setMessage('');
68
+ try {
69
+ var _ref;
70
+ const response = await siteFetch('/api/commerce/checkout', {
71
+ method: 'POST',
72
+ headers: {
73
+ 'Content-Type': 'application/json'
74
+ },
75
+ body: JSON.stringify(_extends({
76
+ hostId,
77
+ productId
78
+ }, coupon.trim() ? {
79
+ couponCode: coupon.trim()
80
+ } : {}))
81
+ });
82
+ const payload = await response.json().catch(()=>({}));
83
+ if (response.ok && (payload == null ? void 0 : payload.url)) {
84
+ // The Starter block's `begin_checkout`. It takes real money through
85
+ // Stripe and reported nothing, so a site built from this block alone
86
+ // showed GA4 a `purchase` with no checkout step in front of it — which
87
+ // reads as a 0% checkout rate rather than as an unmeasured path.
88
+ //
89
+ // Fired only after the server has ACCEPTED the request and returned a
90
+ // session url, never on the click: a count inflated by sold-out
91
+ // products and rejected coupons describes attempts, not checkouts.
92
+ //
93
+ // `value` is the price the SHOPPER SAW, which is the block's own prop.
94
+ // The charge is priced server-side from the product doc — an author
95
+ // who edits one without the other has a storefront that lies to
96
+ // shoppers before it lies to GA — and an unparseable or absent price
97
+ // reports no event at all, because a `value: 0` on a real sale is a
98
+ // worse number than a missing one.
99
+ const shownPrice = Number(priceUsd);
100
+ if (Number.isFinite(shownPrice) && shownPrice > 0) {
101
+ await trackEventBeforeNavigation('begin_checkout', buildBeginCheckoutParams({
102
+ items: [
103
+ {
104
+ item_id: productId,
105
+ item_name: name || 'Product',
106
+ price: shownPrice,
107
+ quantity: 1
108
+ }
109
+ ]
110
+ }));
111
+ }
112
+ window.location.assign(payload.url);
113
+ return;
114
+ }
115
+ // Server messages are visitor-safe (AGL-96): "Sold out", "Invalid
116
+ // or expired coupon", etc.
117
+ setMessage(String((_ref = payload == null ? void 0 : payload.error) != null ? _ref : ''));
118
+ setStatus('error');
119
+ } catch (unused) {
120
+ setStatus('error');
121
+ }
122
+ }, [
123
+ hostId,
124
+ productId,
125
+ status,
126
+ coupon,
127
+ siteFetch
128
+ ]);
129
+ return /*#__PURE__*/ _jsxs(Card, _extends({
130
+ ref: ref,
131
+ variant: "outlined",
132
+ sx: {
133
+ maxWidth: 360
134
+ }
135
+ }, rest, {
136
+ children: [
137
+ imageUrl ? /*#__PURE__*/ _jsx(CardMedia, _extends({
138
+ component: "img",
139
+ image: imageUrl,
140
+ alt: name != null ? name : '',
141
+ sx: {
142
+ height: 200,
143
+ objectFit: 'cover'
144
+ }
145
+ }, Aglyn.DEFERRED_IMAGE_ATTRIBUTES)) : null,
146
+ /*#__PURE__*/ _jsxs(CardContent, {
147
+ children: [
148
+ /*#__PURE__*/ _jsx(Typography, {
149
+ variant: "h6",
150
+ children: name || 'Product name'
151
+ }),
152
+ priceUsd ? /*#__PURE__*/ _jsx(Typography, {
153
+ variant: "subtitle1",
154
+ color: "text.secondary",
155
+ children: `$${priceUsd}`
156
+ }) : null,
157
+ description ? /*#__PURE__*/ _jsx(Typography, {
158
+ variant: "body2",
159
+ sx: {
160
+ mt: 1
161
+ },
162
+ children: description
163
+ }) : null,
164
+ status === 'error' ? /*#__PURE__*/ _jsx(Alert, {
165
+ severity: soldOut ? 'info' : 'error',
166
+ sx: {
167
+ mt: 1
168
+ },
169
+ children: message || 'Checkout is unavailable right now.'
170
+ }) : null,
171
+ showCoupon && !soldOut ? /*#__PURE__*/ _jsx(TextField, {
172
+ size: "small",
173
+ fullWidth: true,
174
+ label: "Coupon code",
175
+ value: coupon,
176
+ onChange: (event)=>setCoupon(event.target.value),
177
+ sx: {
178
+ mt: 1.5
179
+ }
180
+ }) : null,
181
+ /*#__PURE__*/ _jsx(Button, {
182
+ variant: "contained",
183
+ color: "primary",
184
+ sx: {
185
+ mt: 2
186
+ },
187
+ disabled: status === 'sending' || soldOut,
188
+ onClick: handleBuy,
189
+ fullWidth: true,
190
+ children: soldOut ? 'Sold out' : status === 'sending' ? 'Redirecting…' : buyLabel || 'Buy now'
191
+ })
192
+ ]
193
+ })
194
+ ]
195
+ }));
196
+ });
197
+ Product.displayName = 'AglynProduct';
198
+ export const schema = {
199
+ $id: ID,
200
+ pluginId: BUNDLE_ID,
201
+ displayName: 'Product',
202
+ description: 'A single product card with a buy button, priced on the server.',
203
+ category: Aglyn.ComponentCategory.COMMERCE,
204
+ icon: {
205
+ path: mdiCartOutline.path,
206
+ sx: {
207
+ color: '#2e7d32'
208
+ }
209
+ },
210
+ flags: {
211
+ selfClosing: Aglyn.FEATURE_FLAG.ENABLED
212
+ },
213
+ attributes: [
214
+ {
215
+ name: 'productId',
216
+ description: 'Product id from the Products card on the host dashboard — the ' + 'charge uses that product’s price.',
217
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
218
+ label: 'Product id'
219
+ },
220
+ {
221
+ name: 'name',
222
+ description: 'Displayed product name.',
223
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
224
+ label: 'Name'
225
+ },
226
+ {
227
+ name: 'priceUsd',
228
+ description: 'Displayed price (the charge uses the product doc).',
229
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
230
+ label: 'Price (USD)'
231
+ },
232
+ {
233
+ name: 'description',
234
+ description: 'Short product description.',
235
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
236
+ label: 'Description'
237
+ },
238
+ {
239
+ name: 'imageUrl',
240
+ description: 'Product image URL (copy from the Media library).',
241
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
242
+ label: 'Image URL'
243
+ },
244
+ {
245
+ name: 'buyLabel',
246
+ description: 'Buy button label.',
247
+ component: Aglyn.FieldComponentType.TEXT_FIELD,
248
+ label: 'Button label'
249
+ },
250
+ {
251
+ name: 'showCoupon',
252
+ label: 'Show coupon field',
253
+ description: 'Adds a coupon-code input; codes are managed on the Products card.',
254
+ component: Aglyn.FieldComponentType.CHECKBOX
255
+ }
256
+ ]
257
+ };
258
+ export const presets = [
259
+ {
260
+ $id: generatePresetId(ID),
261
+ type: Aglyn.NodeType.PRESET,
262
+ displayName: 'Product',
263
+ pluginId: BUNDLE_ID,
264
+ description: 'Product card with a Stripe Checkout buy button',
265
+ category: Aglyn.ComponentCategory.COMMERCE,
266
+ icon: {
267
+ path: mdiCartOutline.path,
268
+ sx: {
269
+ color: '#2e7d32'
270
+ }
271
+ },
272
+ data: {
273
+ $id: null,
274
+ componentId: ID,
275
+ pluginId: BUNDLE_ID,
276
+ props: {
277
+ name: 'My product',
278
+ priceUsd: '25',
279
+ description: 'A short description of what the buyer gets.',
280
+ buyLabel: 'Buy now'
281
+ }
282
+ }
283
+ }
284
+ ];
285
+ export default Product;
286
+
287
+ //# sourceMappingURL=product.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/product.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 buildBeginCheckoutParams,\n trackEventBeforeNavigation,\n} from '@aglyn/aglyn/app-utils/analytics-events'\nimport { mdiCartOutline } from '@aglyn/shared-data-mdi'\nimport Alert from '@mui/material/Alert'\nimport Button from '@mui/material/Button'\nimport Card from '@mui/material/Card'\nimport CardContent from '@mui/material/CardContent'\nimport CardMedia from '@mui/material/CardMedia'\nimport TextField from '@mui/material/TextField'\nimport Typography from '@mui/material/Typography'\nimport { forwardRef, useCallback, useState } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { generatePresetId } from '../utils/generate-preset-id'\n\n// Component ids are persisted in screen documents; never rename.\nexport const ID: Aglyn.ComponentId = 'product'\n\nexport interface ProductProps {\n /** Id of the product doc under hosts/{hostId}/products. */\n productId?: string\n name?: string\n /** Display price; the charge always uses the product doc's price. */\n priceUsd?: string\n description?: string\n imageUrl?: string\n buyLabel?: string\n /** Shows a coupon-code input above the buy button (AGL-96). */\n showCoupon?: boolean\n}\n\n/**\n * Commerce Starter product block (AGL-90): displays from its own props,\n * but Buy posts {hostId, productId} to the tenant's /api/commerce/checkout,\n * which prices from the server-side product doc — canvas props can't forge\n * a charge. Inert without SiteContext (besigner canvas/preview).\n */\nconst Product = forwardRef<HTMLDivElement, ProductProps>((props, ref) => {\n const {\n productId,\n name,\n priceUsd,\n description,\n imageUrl: storedImageUrl,\n buyLabel,\n showCoupon,\n ...rest\n } = props\n const { hostId } = Aglyn.useSite()\n const siteFetch = Aglyn.useSiteFetch()\n // A media-picker target, so it can hold a media reference (AGL-1215) as\n // well as any URL form that predates it. `siteRelativeMediaSrc` rather\n // than the bare resolver (AGL-1726): one of those older forms is an\n // absolute `https://{subdomain}.aglyn.app/api/media/cdn/…`, which on a\n // white-label storefront names our platform in the customer's page source\n // and fetches cross-origin from their own domain.\n const imageUrl = Aglyn.siteRelativeMediaSrc(storedImageUrl, { hostId })\n const [status, setStatus] = useState<'idle' | 'sending' | 'error'>('idle')\n const [coupon, setCoupon] = useState('')\n const [message, setMessage] = useState('')\n const soldOut = message === 'Sold out'\n\n const handleBuy = useCallback(async () => {\n if (!hostId || !productId || status === 'sending') return\n setStatus('sending')\n setMessage('')\n try {\n const response = await siteFetch('/api/commerce/checkout', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n hostId,\n productId,\n ...(coupon.trim() ? { couponCode: coupon.trim() } : {}),\n }),\n })\n const payload = await response.json().catch(() => ({}))\n if (response.ok && payload?.url) {\n // The Starter block's `begin_checkout`. It takes real money through\n // Stripe and reported nothing, so a site built from this block alone\n // showed GA4 a `purchase` with no checkout step in front of it — which\n // reads as a 0% checkout rate rather than as an unmeasured path.\n //\n // Fired only after the server has ACCEPTED the request and returned a\n // session url, never on the click: a count inflated by sold-out\n // products and rejected coupons describes attempts, not checkouts.\n //\n // `value` is the price the SHOPPER SAW, which is the block's own prop.\n // The charge is priced server-side from the product doc — an author\n // who edits one without the other has a storefront that lies to\n // shoppers before it lies to GA — and an unparseable or absent price\n // reports no event at all, because a `value: 0` on a real sale is a\n // worse number than a missing one.\n const shownPrice = Number(priceUsd)\n if (Number.isFinite(shownPrice) && shownPrice > 0) {\n await trackEventBeforeNavigation(\n 'begin_checkout',\n buildBeginCheckoutParams({\n items: [\n {\n item_id: productId,\n item_name: name || 'Product',\n price: shownPrice,\n quantity: 1,\n },\n ],\n }),\n )\n }\n window.location.assign(payload.url)\n return\n }\n // Server messages are visitor-safe (AGL-96): \"Sold out\", \"Invalid\n // or expired coupon\", etc.\n setMessage(String(payload?.error ?? ''))\n setStatus('error')\n } catch {\n setStatus('error')\n }\n }, [hostId, productId, status, coupon, siteFetch])\n\n return (\n <Card ref={ref} variant=\"outlined\" sx={{ maxWidth: 360 }} {...rest}>\n {imageUrl ? (\n <CardMedia\n component=\"img\"\n image={imageUrl}\n alt={name ?? ''}\n sx={{ height: 200, objectFit: 'cover' }}\n // Deferred (AGL-2486). A Product card is a 360px-wide card dropped\n // into a page; it is never the page's lead image, and before this\n // it carried no loading hint at all — i.e. EAGER at default\n // priority, outranking the `lazy` + `low` Image elements in the\n // section the reader was actually looking at.\n {...Aglyn.DEFERRED_IMAGE_ATTRIBUTES}\n />\n ) : null}\n <CardContent>\n <Typography variant=\"h6\">{name || 'Product name'}</Typography>\n {priceUsd ? (\n <Typography variant=\"subtitle1\" color=\"text.secondary\">\n {`$${priceUsd}`}\n </Typography>\n ) : null}\n {description ? (\n <Typography variant=\"body2\" sx={{ mt: 1 }}>\n {description}\n </Typography>\n ) : null}\n {status === 'error' ? (\n <Alert severity={soldOut ? 'info' : 'error'} sx={{ mt: 1 }}>\n {message || 'Checkout is unavailable right now.'}\n </Alert>\n ) : null}\n {showCoupon && !soldOut ? (\n <TextField\n size=\"small\"\n fullWidth\n label=\"Coupon code\"\n value={coupon}\n onChange={(event) => setCoupon(event.target.value)}\n sx={{ mt: 1.5 }}\n />\n ) : null}\n <Button\n variant=\"contained\"\n color=\"primary\"\n sx={{ mt: 2 }}\n disabled={status === 'sending' || soldOut}\n onClick={handleBuy}\n fullWidth\n >\n {soldOut\n ? 'Sold out'\n : status === 'sending'\n ? 'Redirecting…'\n : buyLabel || 'Buy now'}\n </Button>\n </CardContent>\n </Card>\n )\n})\nProduct.displayName = 'AglynProduct'\n\nexport const schema: Aglyn.ComponentSchema<ProductProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Product',\n description: 'A single product card with a buy button, priced on the server.',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiCartOutline.path, sx: { color: '#2e7d32' } },\n flags: { selfClosing: Aglyn.FEATURE_FLAG.ENABLED },\n attributes: [\n {\n name: 'productId',\n description:\n 'Product id from the Products card on the host dashboard — the ' +\n 'charge uses that product’s price.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Product id',\n },\n {\n name: 'name',\n description: 'Displayed product name.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Name',\n },\n {\n name: 'priceUsd',\n description: 'Displayed price (the charge uses the product doc).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Price (USD)',\n },\n {\n name: 'description',\n description: 'Short product description.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Description',\n },\n {\n name: 'imageUrl',\n description: 'Product image URL (copy from the Media library).',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Image URL',\n },\n {\n name: 'buyLabel',\n description: 'Buy button label.',\n component: Aglyn.FieldComponentType.TEXT_FIELD,\n label: 'Button label',\n },\n {\n name: 'showCoupon',\n label: 'Show coupon field',\n description:\n 'Adds a coupon-code input; codes are managed on the Products card.',\n component: Aglyn.FieldComponentType.CHECKBOX,\n },\n ],\n}\n\nexport const presets: Aglyn.PresetSchema[] = [\n {\n $id: generatePresetId(ID),\n type: Aglyn.NodeType.PRESET,\n displayName: 'Product',\n pluginId: BUNDLE_ID,\n description: 'Product card with a Stripe Checkout buy button',\n category: Aglyn.ComponentCategory.COMMERCE,\n icon: { path: mdiCartOutline.path, sx: { color: '#2e7d32' } },\n data: {\n $id: null,\n componentId: ID,\n pluginId: BUNDLE_ID,\n props: {\n name: 'My product',\n priceUsd: '25',\n description: 'A short description of what the buyer gets.',\n buyLabel: 'Buy now',\n },\n },\n },\n]\n\nexport default Product\n"],"names":["Aglyn","buildBeginCheckoutParams","trackEventBeforeNavigation","mdiCartOutline","Alert","Button","Card","CardContent","CardMedia","TextField","Typography","forwardRef","useCallback","useState","BUNDLE_ID","generatePresetId","ID","Product","props","ref","productId","name","priceUsd","description","imageUrl","storedImageUrl","buyLabel","showCoupon","rest","hostId","useSite","siteFetch","useSiteFetch","siteRelativeMediaSrc","status","setStatus","coupon","setCoupon","message","setMessage","soldOut","handleBuy","response","method","headers","body","JSON","stringify","trim","couponCode","payload","json","catch","ok","url","shownPrice","Number","isFinite","items","item_id","item_name","price","quantity","window","location","assign","String","error","variant","sx","maxWidth","component","image","alt","height","objectFit","DEFERRED_IMAGE_ATTRIBUTES","color","mt","severity","size","fullWidth","label","value","onChange","event","target","disabled","onClick","displayName","schema","$id","pluginId","category","ComponentCategory","COMMERCE","icon","path","flags","selfClosing","FEATURE_FLAG","ENABLED","attributes","FieldComponentType","TEXT_FIELD","CHECKBOX","presets","type","NodeType","PRESET","data","componentId"],"mappings":"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"}
@@ -0,0 +1,57 @@
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 * as Aglyn from '@aglyn/aglyn';
18
+ import { type BoxProps } from '@mui/material/Box';
19
+ export declare const ID: Aglyn.ComponentId;
20
+ export interface ReusableInstanceProps extends BoxProps {
21
+ /** Definition id in `hosts/{hostId}/components`; grafted at render time. */
22
+ refId?: string;
23
+ /**
24
+ * The definition's display name, carried on the instance purely so the
25
+ * editor placeholder can say which component this stands for (AGL-1193).
26
+ * Never rendered on production surfaces, where the graft fills the box.
27
+ */
28
+ name?: string;
29
+ /**
30
+ * This instance's declared-prop overrides (AGL-1247). Addressed to the
31
+ * GRAFT, which reads it off the stored node to substitute `{{prop.*}}`
32
+ * inside its private copy of the definition — the wrapper element itself
33
+ * has no use for it, and it is stripped below rather than spread.
34
+ */
35
+ propValues?: Record<string, unknown>;
36
+ }
37
+ /**
38
+ * Wrapper element for a reusable-component instance. On production surfaces
39
+ * the compose step (`composeReusableComponentNodes`) grafts the definition
40
+ * subtree inside, so this renders as a plain container. In the editor the
41
+ * instance has no children (definitions aren't grafted into the editable
42
+ * canvas), so the CSS `:empty` placeholder marks it visibly instead — named,
43
+ * since a layout whose chrome has been promoted is otherwise nothing but
44
+ * indistinguishable dashed boxes.
45
+ *
46
+ * The placeholder is drawn on editing surfaces only. A published instance
47
+ * whose definition does not resolve is left empty by the compose step, and a
48
+ * visitor must not see the marker there.
49
+ */
50
+ declare const ReusableInstance: import("react").ForwardRefExoticComponent<Omit<ReusableInstanceProps, "ref"> & import("react").RefAttributes<any>>;
51
+ export declare const schema: Aglyn.ComponentSchema<ReusableInstanceProps>;
52
+ /**
53
+ * No static presets: instances are inserted from per-host definitions, which
54
+ * the console registers dynamically (category "Your components").
55
+ */
56
+ export declare const presets: Aglyn.PresetSchema[];
57
+ export default ReusableInstance;
@@ -0,0 +1,126 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
3
+ import { _ as _to_property_key } from "@swc/helpers/_/_to_property_key";
4
+ import { jsx as _jsx } from "react/jsx-runtime";
5
+ /**
6
+ * @license
7
+ * Copyright 2026 Aglyn LLC
8
+ *
9
+ * Licensed under the Apache License, Version 2.0 (the "License");
10
+ * you may not use this file except in compliance with the License.
11
+ * You may obtain a copy of the License at
12
+ *
13
+ * http://www.apache.org/licenses/LICENSE-2.0
14
+ *
15
+ * Unless required by applicable law or agreed to in writing, software
16
+ * distributed under the License is distributed on an "AS IS" BASIS,
17
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ * See the License for the specific language governing permissions and
19
+ * limitations under the License.
20
+ */ import * as Aglyn from "@aglyn/aglyn";
21
+ import { mdiPackageVariant } from "@aglyn/shared-data-mdi";
22
+ import Box from "@mui/material/Box";
23
+ import { forwardRef, useContext } from "react";
24
+ import { BUNDLE_ID } from "../constants/bundle-common.js";
25
+ // Persisted in screen documents; never rename (see AGL-34 ADR).
26
+ export const ID = Aglyn.REUSABLE_INSTANCE_COMPONENT_ID;
27
+ /**
28
+ * Wrapper element for a reusable-component instance. On production surfaces
29
+ * the compose step (`composeReusableComponentNodes`) grafts the definition
30
+ * subtree inside, so this renders as a plain container. In the editor the
31
+ * instance has no children (definitions aren't grafted into the editable
32
+ * canvas), so the CSS `:empty` placeholder marks it visibly instead — named,
33
+ * since a layout whose chrome has been promoted is otherwise nothing but
34
+ * indistinguishable dashed boxes.
35
+ *
36
+ * The placeholder is drawn on editing surfaces only. A published instance
37
+ * whose definition does not resolve is left empty by the compose step, and a
38
+ * visitor must not see the marker there.
39
+ */ const ReusableInstance = /*#__PURE__*/ forwardRef((props, ref)=>{
40
+ const _key = Aglyn.REUSABLE_INSTANCE_PROP_VALUES_KEY, { refId: _refId, name, // Instance-scoped directives, not element attributes. `refId` and
41
+ // `name` were always stripped; `propValues` was not, so the author's
42
+ // overrides spread onto the Box and reached the DOM — React logs
43
+ // "does not recognize the `propValues` prop" on the canvas, and a
44
+ // published page serialised the same copy a second time as
45
+ // `propvalues="[object Object]"` on the wrapper (AGL-2486).
46
+ //
47
+ // Stripped HERE rather than in the graft because the canvas renders
48
+ // an instance the graft has not touched (definitions are not grafted
49
+ // into the editable canvas), so a strip upstream would fix the
50
+ // published page and leave the editor warning on every render.
51
+ [_key]: _propValues, children, sx } = props, rest = _object_without_properties_loose(props, [
52
+ "refId",
53
+ "name",
54
+ _to_property_key(_key),
55
+ "children",
56
+ "sx"
57
+ ]);
58
+ const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext);
59
+ return /*#__PURE__*/ _jsx(Box, _extends({
60
+ ref: ref,
61
+ // `content: attr()` rather than a child, so the box stays `:empty`
62
+ // and the placeholder cannot be mistaken for grafted content.
63
+ "data-aglyn-component": name || 'Reusable component',
64
+ sx: [
65
+ Boolean(suppressNavigation) && {
66
+ '&:empty': {
67
+ minHeight: 56,
68
+ m: 1,
69
+ display: 'flex',
70
+ alignItems: 'center',
71
+ justifyContent: 'center',
72
+ borderWidth: 1,
73
+ borderStyle: 'dashed',
74
+ borderColor: 'divider',
75
+ borderRadius: 1
76
+ },
77
+ '&:empty::after': {
78
+ content: 'attr(data-aglyn-component)',
79
+ color: 'text.secondary',
80
+ fontSize: 12,
81
+ letterSpacing: 1,
82
+ textTransform: 'uppercase'
83
+ }
84
+ },
85
+ ...Array.isArray(sx) ? sx : sx ? [
86
+ sx
87
+ ] : []
88
+ ]
89
+ }, rest, {
90
+ children: children
91
+ }));
92
+ });
93
+ ReusableInstance.displayName = 'ReusableInstance';
94
+ export const schema = {
95
+ $id: ID,
96
+ pluginId: BUNDLE_ID,
97
+ displayName: 'Reusable Component',
98
+ description: 'Where a reusable component is placed. Edit the component once and every instance follows.',
99
+ category: Aglyn.ComponentCategory.SURFACE,
100
+ icon: {
101
+ path: mdiPackageVariant.path,
102
+ sx: {
103
+ color: '#9c27b0'
104
+ }
105
+ },
106
+ flags: {
107
+ // Instances are opaque: their content lives in the definition, so
108
+ // nothing may be dropped inside from the canvas.
109
+ dropping: Aglyn.FEATURE_FLAG.DISABLED
110
+ },
111
+ /*
112
+ * No attributes. An instance is a REFERENCE, not an element: the graft puts
113
+ * the definition's root in its place, so the only node that reaches a
114
+ * published page is the component's own (AGL-2521). An element picker here
115
+ * briefly existed (AGL-2514) and configured a wrapper that no longer
116
+ * exists — what it was reaching for is **Attribute overrides → Component
117
+ * root**, which sets the definition root's element for this placement.
118
+ */ attributes: []
119
+ };
120
+ /**
121
+ * No static presets: instances are inserted from per-host definitions, which
122
+ * the console registers dynamically (category "Your components").
123
+ */ export const presets = [];
124
+ export default ReusableInstance;
125
+
126
+ //# sourceMappingURL=reusable-instance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/mui/src/lib/components/reusable-instance.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 { mdiPackageVariant } from '@aglyn/shared-data-mdi'\nimport Box, { type BoxProps } from '@mui/material/Box'\nimport { forwardRef, useContext } from 'react'\nimport { BUNDLE_ID } from '../constants/bundle-common'\n\n// Persisted in screen documents; never rename (see AGL-34 ADR).\nexport const ID: Aglyn.ComponentId = Aglyn.REUSABLE_INSTANCE_COMPONENT_ID\n\nexport interface ReusableInstanceProps extends BoxProps {\n /** Definition id in `hosts/{hostId}/components`; grafted at render time. */\n refId?: string\n /**\n * The definition's display name, carried on the instance purely so the\n * editor placeholder can say which component this stands for (AGL-1193).\n * Never rendered on production surfaces, where the graft fills the box.\n */\n name?: string\n /**\n * This instance's declared-prop overrides (AGL-1247). Addressed to the\n * GRAFT, which reads it off the stored node to substitute `{{prop.*}}`\n * inside its private copy of the definition — the wrapper element itself\n * has no use for it, and it is stripped below rather than spread.\n */\n propValues?: Record<string, unknown>\n}\n\n/**\n * Wrapper element for a reusable-component instance. On production surfaces\n * the compose step (`composeReusableComponentNodes`) grafts the definition\n * subtree inside, so this renders as a plain container. In the editor the\n * instance has no children (definitions aren't grafted into the editable\n * canvas), so the CSS `:empty` placeholder marks it visibly instead — named,\n * since a layout whose chrome has been promoted is otherwise nothing but\n * indistinguishable dashed boxes.\n *\n * The placeholder is drawn on editing surfaces only. A published instance\n * whose definition does not resolve is left empty by the compose step, and a\n * visitor must not see the marker there.\n */\nconst ReusableInstance = forwardRef<any, ReusableInstanceProps>(\n (props, ref) => {\n const {\n refId: _refId,\n name,\n // Instance-scoped directives, not element attributes. `refId` and\n // `name` were always stripped; `propValues` was not, so the author's\n // overrides spread onto the Box and reached the DOM — React logs\n // \"does not recognize the `propValues` prop\" on the canvas, and a\n // published page serialised the same copy a second time as\n // `propvalues=\"[object Object]\"` on the wrapper (AGL-2486).\n //\n // Stripped HERE rather than in the graft because the canvas renders\n // an instance the graft has not touched (definitions are not grafted\n // into the editable canvas), so a strip upstream would fix the\n // published page and leave the editor warning on every render.\n [Aglyn.REUSABLE_INSTANCE_PROP_VALUES_KEY]: _propValues,\n children,\n sx,\n ...rest\n } = props\n const { suppressNavigation } = useContext(Aglyn.ScreenLinkContext)\n return (\n <Box\n ref={ref}\n // `content: attr()` rather than a child, so the box stays `:empty`\n // and the placeholder cannot be mistaken for grafted content.\n data-aglyn-component={name || 'Reusable component'}\n sx={[\n Boolean(suppressNavigation) && {\n '&:empty': {\n minHeight: 56,\n m: 1,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 1,\n borderStyle: 'dashed',\n borderColor: 'divider',\n borderRadius: 1,\n },\n '&:empty::after': {\n content: 'attr(data-aglyn-component)',\n color: 'text.secondary',\n fontSize: 12,\n letterSpacing: 1,\n textTransform: 'uppercase',\n },\n },\n ...(Array.isArray(sx) ? sx : sx ? [sx] : []),\n ]}\n {...rest}\n >\n {children}\n </Box>\n )\n },\n)\nReusableInstance.displayName = 'ReusableInstance'\n\nexport const schema: Aglyn.ComponentSchema<ReusableInstanceProps> = {\n $id: ID,\n pluginId: BUNDLE_ID,\n displayName: 'Reusable Component',\n description:\n 'Where a reusable component is placed. Edit the component once and every instance follows.',\n category: Aglyn.ComponentCategory.SURFACE,\n icon: {\n path: mdiPackageVariant.path,\n sx: { color: '#9c27b0' },\n },\n flags: {\n // Instances are opaque: their content lives in the definition, so\n // nothing may be dropped inside from the canvas.\n dropping: Aglyn.FEATURE_FLAG.DISABLED,\n },\n /*\n * No attributes. An instance is a REFERENCE, not an element: the graft puts\n * the definition's root in its place, so the only node that reaches a\n * published page is the component's own (AGL-2521). An element picker here\n * briefly existed (AGL-2514) and configured a wrapper that no longer\n * exists — what it was reaching for is **Attribute overrides → Component\n * root**, which sets the definition root's element for this placement.\n */\n attributes: [],\n}\n\n/**\n * No static presets: instances are inserted from per-host definitions, which\n * the console registers dynamically (category \"Your components\").\n */\nexport const presets: Aglyn.PresetSchema[] = []\n\nexport default ReusableInstance\n"],"names":["Aglyn","mdiPackageVariant","Box","forwardRef","useContext","BUNDLE_ID","ID","REUSABLE_INSTANCE_COMPONENT_ID","ReusableInstance","props","ref","REUSABLE_INSTANCE_PROP_VALUES_KEY","refId","_refId","name","_propValues","children","sx","rest","suppressNavigation","ScreenLinkContext","data-aglyn-component","Boolean","minHeight","m","display","alignItems","justifyContent","borderWidth","borderStyle","borderColor","borderRadius","content","color","fontSize","letterSpacing","textTransform","Array","isArray","displayName","schema","$id","pluginId","description","category","ComponentCategory","SURFACE","icon","path","flags","dropping","FEATURE_FLAG","DISABLED","attributes","presets"],"mappings":";;;;AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,iBAAiB,QAAQ,yBAAwB;AAC1D,OAAOC,SAA4B,oBAAmB;AACtD,SAASC,UAAU,EAAEC,UAAU,QAAQ,QAAO;AAC9C,SAASC,SAAS,QAAQ,gCAA4B;AAEtD,gEAAgE;AAChE,OAAO,MAAMC,KAAwBN,MAAMO,8BAA8B,CAAA;AAoBzE;;;;;;;;;;;;CAYC,GACD,MAAMC,iCAAmBL,WACvB,CAACM,OAAOC;IACN,aAcGV,MAAMW,iCAAiC,EAdpC,EACJC,OAAOC,MAAM,EACbC,IAAI,EACJ,kEAAkE;IAClE,qEAAqE;IACrE,iEAAiE;IACjE,kEAAkE;IAClE,2DAA2D;IAC3D,4DAA4D;IAC5D,EAAE;IACF,oEAAoE;IACpE,qEAAqE;IACrE,+DAA+D;IAC/D,+DAA+D;IAC/D,MAAyC,EAAEC,WAAW,EACtDC,QAAQ,EACRC,EAAE,EAEH,GAAGR,OADCS,wCACDT;;;;;;;IACJ,MAAM,EAAEU,kBAAkB,EAAE,GAAGf,WAAWJ,MAAMoB,iBAAiB;IACjE,qBACE,KAAClB;QACCQ,KAAKA;QACL,mEAAmE;QACnE,8DAA8D;QAC9DW,wBAAsBP,QAAQ;QAC9BG,IAAI;YACFK,QAAQH,uBAAuB;gBAC7B,WAAW;oBACTI,WAAW;oBACXC,GAAG;oBACHC,SAAS;oBACTC,YAAY;oBACZC,gBAAgB;oBAChBC,aAAa;oBACbC,aAAa;oBACbC,aAAa;oBACbC,cAAc;gBAChB;gBACA,kBAAkB;oBAChBC,SAAS;oBACTC,OAAO;oBACPC,UAAU;oBACVC,eAAe;oBACfC,eAAe;gBACjB;YACF;eACIC,MAAMC,OAAO,CAACrB,MAAMA,KAAKA,KAAK;gBAACA;aAAG,GAAG,EAAE;SAC5C;OACGC;kBAEHF;;AAGP;AAEFR,iBAAiB+B,WAAW,GAAG;AAE/B,OAAO,MAAMC,SAAuD;IAClEC,KAAKnC;IACLoC,UAAUrC;IACVkC,aAAa;IACbI,aACE;IACFC,UAAU5C,MAAM6C,iBAAiB,CAACC,OAAO;IACzCC,MAAM;QACJC,MAAM/C,kBAAkB+C,IAAI;QAC5B/B,IAAI;YAAEgB,OAAO;QAAU;IACzB;IACAgB,OAAO;QACL,kEAAkE;QAClE,iDAAiD;QACjDC,UAAUlD,MAAMmD,YAAY,CAACC,QAAQ;IACvC;IACA;;;;;;;GAOC,GACDC,YAAY,EAAE;AAChB,EAAC;AAED;;;CAGC,GACD,OAAO,MAAMC,UAAgC,EAAE,CAAA;AAE/C,eAAe9C,iBAAgB"}
@@ -0,0 +1,85 @@
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 * as Aglyn from '@aglyn/aglyn';
18
+ import { type ButtonProps } from '@mui/material/Button';
19
+ import { type LinkTargetChoice } from '../utils/link-target-props';
20
+ export declare const ID: Aglyn.ComponentId;
21
+ export interface ScreenLinkProps extends ButtonProps {
22
+ /**
23
+ * Target screen id. The href is resolved from the host routing map at
24
+ * render time (see `ScreenLinkContext`), so slug renames and re-parenting
25
+ * never break the link — which is what makes navigation inside a shared
26
+ * layout viable.
27
+ */
28
+ screenId?: string;
29
+ /** External URL escape hatch, used only when no `screenId` is set. */
30
+ href?: string;
31
+ /**
32
+ * Which element this ships as (AGL-1195, AGL-1347). Persisted in screen
33
+ * documents; never rename, and `undefined` must keep meaning `'button'` —
34
+ * every link authored before this existed is one.
35
+ *
36
+ * - `'button'` (or unset) — `<a role="button">` with the button's look.
37
+ * - `'link'` — a plain text link; the button-only props are dropped.
38
+ * - `'linkButton'` — a plain `<a>` with NO role, wearing the button's
39
+ * `variant`/`size`/`fullWidth`. Appearance without semantics: a pill
40
+ * that navigates announces as the link it is.
41
+ */
42
+ renderAs?: 'button' | 'link' | 'linkButton';
43
+ /**
44
+ * Where the link opens. Persisted in screen documents; never rename a
45
+ * value, and `undefined` must keep meaning `'_self'` — every link authored
46
+ * before this existed stays in the tab.
47
+ */
48
+ target?: LinkTargetChoice;
49
+ /** Window name used only when `target` is `'custom'`. */
50
+ targetName?: string;
51
+ /**
52
+ * Sanitized emphasis markup for the label, written by the inline editor's
53
+ * rich mode (AGL-2557). `children` keeps the plain-text reading of it, and
54
+ * is what every surface that cannot render markup falls back to.
55
+ */
56
+ html?: string;
57
+ }
58
+ /**
59
+ * Link that targets a screen by id, never a hardcoded path. Degrades to a
60
+ * plain button when the id doesn't resolve (unpublished/deleted screen) and
61
+ * when navigation is suppressed (besigner canvas, preview).
62
+ *
63
+ * `renderAs="link"` renders MUI's `Link` instead of `Button` (AGL-1195).
64
+ * Footer and inline navigation are *text*: as buttons they inherit button
65
+ * typography, uppercase transforms and the wrong role for assistive tech.
66
+ * The button-only props are dropped in that mode rather than passed through
67
+ * — `variant` means the typography variant on a `Link`, so forwarding
68
+ * `"contained"` would silently produce an unstyled element.
69
+ *
70
+ * `renderAs="linkButton"` splits the two halves that used to travel together
71
+ * (AGL-1347). A pill row whose chips all navigate — docs URLs, `mailto:`,
72
+ * social profiles — is a legitimate design, but every chip announced as a
73
+ * button, which tells a screen-reader user to expect an action and costs
74
+ * them the link-list and open-in-new-tab affordances. This mode keeps the
75
+ * button's `variant`/`size`/`fullWidth` and drops the role, exactly as MUI's
76
+ * own `Button component={Link}` does. It is a THIRD value rather than
77
+ * `variant` becoming live in `"link"` mode, because a node switched to Text
78
+ * link keeps whatever `variant` it was authored with (the shipped preset is
79
+ * `variant: 'outlined'`) — honouring those would repaint the site's nav and
80
+ * footer as outlined pills.
81
+ */
82
+ declare const ScreenLink: import("react").ForwardRefExoticComponent<Omit<ScreenLinkProps, "ref"> & import("react").RefAttributes<any>>;
83
+ export declare const schema: Aglyn.ComponentSchema<ScreenLinkProps>;
84
+ export declare const presets: Aglyn.PresetSchema[];
85
+ export default ScreenLink;