| @@ -110,9 +110,87 @@ | ||
| 110 | 110 | }; |
| 111 | 111 | }); |
| 112 | 112 | } |
| 113 | 113 | |
| 114 | +// packages/widget-primitives/build-module/icon-resolver/icon-resolver.mjs | |
| 115 | +var iconResolver; | |
| 116 | +function registerIconResolver(resolver) { | |
| 117 | + if (iconResolver) { | |
| 118 | + return void 0; | |
| 119 | + } | |
| 120 | + iconResolver = resolver; | |
| 121 | + return resolver; | |
| 122 | +} | |
| 123 | +async function resolveIcon(reference) { | |
| 124 | + if (!iconResolver) { | |
| 125 | + return null; | |
| 126 | + } | |
| 127 | + try { | |
| 128 | + return await iconResolver(reference); | |
| 129 | + } catch { | |
| 130 | + return null; | |
| 131 | + } | |
| 132 | +} | |
| 133 | + | |
| 114 | 134 | // packages/widget-primitives/build-module/hooks/use-widget-types.mjs |
| 135 | +var pendingIcon = (0, import_element2.createElement)("svg", { | |
| 136 | + viewBox: "0 0 24 24" | |
| 137 | +}); | |
| 138 | +function withRenderableIcons(actions, holdPending) { | |
| 139 | + return actions.map(({ icon, ...action }) => { | |
| 140 | + if ((0, import_element2.isValidElement)(icon)) { | |
| 141 | + return { ...action, icon }; | |
| 142 | + } | |
| 143 | + if (holdPending && typeof icon === "string") { | |
| 144 | + return { ...action, icon: pendingIcon }; | |
| 145 | + } | |
| 146 | + return action; | |
| 147 | + }); | |
| 148 | +} | |
| 149 | +function mergeAttributes(recordAttributes, moduleAttributes) { | |
| 150 | + if (!recordAttributes) { | |
| 151 | + return moduleAttributes; | |
| 152 | + } | |
| 153 | + if (!moduleAttributes) { | |
| 154 | + return recordAttributes; | |
| 155 | + } | |
| 156 | + const moduleById = new Map( | |
| 157 | + moduleAttributes.map( | |
| 158 | + (attribute) => [attribute.id, attribute] | |
| 159 | + ) | |
| 160 | + ); | |
| 161 | + const recordIds = new Set( | |
| 162 | + recordAttributes.map((attribute) => attribute.id) | |
| 163 | + ); | |
| 164 | + return [ | |
| 165 | + ...recordAttributes.map((attribute) => { | |
| 166 | + const moduleAttribute = moduleById.get(attribute.id); | |
| 167 | + if (!moduleAttribute?.isValid || !attribute.isValid) { | |
| 168 | + return { ...moduleAttribute, ...attribute }; | |
| 169 | + } | |
| 170 | + return { | |
| 171 | + ...moduleAttribute, | |
| 172 | + ...attribute, | |
| 173 | + isValid: { ...moduleAttribute.isValid, ...attribute.isValid } | |
| 174 | + }; | |
| 175 | + }), | |
| 176 | + ...moduleAttributes.filter( | |
| 177 | + (attribute) => !recordIds.has(attribute.id) | |
| 178 | + ) | |
| 179 | + ]; | |
| 180 | +} | |
| 181 | +var DEFAULT_API_VERSION = 1; | |
| 182 | +function recordOverlay(record) { | |
| 183 | + return { | |
| 184 | + name: record.name, | |
| 185 | + renderModule: record.render_module ?? "", | |
| 186 | + ...record.presentation ? { presentation: record.presentation } : {}, | |
| 187 | + ...record.category ? { category: record.category } : {}, | |
| 188 | + ...record.description ? { description: record.description } : {}, | |
| 189 | + ...record.help ? { help: record.help } : {}, | |
| 190 | + ...record.keywords ? { keywords: record.keywords } : {} | |
| 191 | + }; | |
| 192 | +} | |
| 115 | 193 | function useWidgetTypes(records) { |
| 116 | 194 | const [widgetTypes, setWidgetTypes] = (0, import_element2.useState)([]); |
| 117 | 195 | const [isResolvingWidgetTypes, setIsResolvingWidgetTypes] = (0, import_element2.useState)(true); |
| 118 | 196 | (0, import_element2.useEffect)(() => { |
| @@ -129,9 +207,28 @@ | ||
| 129 | 207 | setIsResolvingWidgetTypes(true); |
| 130 | 208 | Promise.all( |
| 131 | 209 | records.map(async (record) => { |
| 132 | 210 | if (!record.widget_module) { |
| 133 | - return null; | |
| 211 | + if (!record.render_module) { | |
| 212 | + return null; | |
| 213 | + } | |
| 214 | + return { | |
| 215 | + apiVersion: DEFAULT_API_VERSION, | |
| 216 | + title: record.title ?? record.name, | |
| 217 | + ...record.attributes ? { | |
| 218 | + attributes: resolveFields( | |
| 219 | + record.attributes | |
| 220 | + ) | |
| 221 | + } : {}, | |
| 222 | + ...record.icon ? { icon: pendingIcon } : {}, | |
| 223 | + ...record.actions ? { | |
| 224 | + actions: withRenderableIcons( | |
| 225 | + record.actions, | |
| 226 | + true | |
| 227 | + ) | |
| 228 | + } : {}, | |
| 229 | + ...recordOverlay(record) | |
| 230 | + }; | |
| 134 | 231 | } |
| 135 | 232 | try { |
| 136 | 233 | const module = await import( |
| 137 | 234 | /* webpackIgnore: true */ |
| @@ -140,17 +237,20 @@ | ||
| 140 | 237 | if (!module?.default) { |
| 141 | 238 | return null; |
| 142 | 239 | } |
| 143 | 240 | const metadata = module.default; |
| 241 | + const moduleIcon = (0, import_element2.isValidElement)(metadata.icon) ? metadata.icon : void 0; | |
| 242 | + const icon = moduleIcon ?? (record.icon ? pendingIcon : void 0); | |
| 243 | + const actions = record.actions ?? metadata.actions; | |
| 244 | + const attributes = mergeAttributes( | |
| 245 | + record.attributes, | |
| 246 | + metadata.attributes | |
| 247 | + ); | |
| 144 | 248 | return { |
| 249 | + apiVersion: DEFAULT_API_VERSION, | |
| 145 | 250 | ...metadata, |
| 146 | - ...metadata.attributes ? { | |
| 147 | - attributes: resolveFields( | |
| 148 | - metadata.attributes | |
| 149 | - ) | |
| 150 | - } : {}, | |
| 151 | - name: record.name, | |
| 152 | - renderModule: record.render_module ?? "", | |
| 251 | + ...attributes ? { attributes: resolveFields(attributes) } : {}, | |
| 252 | + icon, | |
| 153 | 253 | /* |
| 154 | 254 | * `title` is required: |
| 155 | 255 | * - Server-side title wins |
| 156 | 256 | * - Then the module's title |
| @@ -156,14 +256,15 @@ | ||
| 156 | 256 | * - Then the module's title |
| 157 | 257 | * - Then the record's name as fallback |
| 158 | 258 | */ |
| 159 | 259 | title: record.title ?? metadata.title ?? record.name, |
| 160 | - ...record.presentation ? { presentation: record.presentation } : {}, | |
| 161 | - ...record.category ? { category: record.category } : {}, | |
| 162 | - ...record.description ? { description: record.description } : {}, | |
| 163 | - ...record.help ? { help: record.help } : {}, | |
| 164 | - ...record.keywords ? { keywords: record.keywords } : {}, | |
| 165 | - ...record.actions ? { actions: record.actions } : {} | |
| 260 | + ...actions ? { | |
| 261 | + actions: withRenderableIcons( | |
| 262 | + actions, | |
| 263 | + actions === record.actions | |
| 264 | + ) | |
| 265 | + } : {}, | |
| 266 | + ...recordOverlay(record) | |
| 166 | 267 | }; |
| 167 | 268 | } catch { |
| 168 | 269 | return null; |
| 169 | 270 | } |
| @@ -175,8 +276,63 @@ | ||
| 175 | 276 | setWidgetTypes( |
| 176 | 277 | results.filter((t) => t !== null) |
| 177 | 278 | ); |
| 178 | 279 | setIsResolvingWidgetTypes(false); |
| 280 | + for (const record of records) { | |
| 281 | + if (!record.icon) { | |
| 282 | + continue; | |
| 283 | + } | |
| 284 | + void resolveIcon(record.icon).then((resolved) => { | |
| 285 | + if (cancelled) { | |
| 286 | + return; | |
| 287 | + } | |
| 288 | + setWidgetTypes( | |
| 289 | + (prev) => prev.map((widgetType) => { | |
| 290 | + if (widgetType.name !== record.name) { | |
| 291 | + return widgetType; | |
| 292 | + } | |
| 293 | + if (resolved) { | |
| 294 | + return { ...widgetType, icon: resolved }; | |
| 295 | + } | |
| 296 | + return widgetType.icon === pendingIcon ? { ...widgetType, icon: void 0 } : widgetType; | |
| 297 | + }) | |
| 298 | + ); | |
| 299 | + }); | |
| 300 | + } | |
| 301 | + for (const record of records) { | |
| 302 | + for (const action of record.actions ?? []) { | |
| 303 | + if (typeof action.icon !== "string") { | |
| 304 | + continue; | |
| 305 | + } | |
| 306 | + void resolveIcon(action.icon).then((resolved) => { | |
| 307 | + if (cancelled) { | |
| 308 | + return; | |
| 309 | + } | |
| 310 | + setWidgetTypes( | |
| 311 | + (prev) => prev.map((type) => { | |
| 312 | + if (type.name !== record.name) { | |
| 313 | + return type; | |
| 314 | + } | |
| 315 | + return { | |
| 316 | + ...type, | |
| 317 | + actions: type.actions?.map((entry) => { | |
| 318 | + if (entry.id !== action.id) { | |
| 319 | + return entry; | |
| 320 | + } | |
| 321 | + if (resolved) { | |
| 322 | + return { | |
| 323 | + ...entry, | |
| 324 | + icon: resolved | |
| 325 | + }; | |
| 326 | + } | |
| 327 | + return entry.icon === pendingIcon ? { ...entry, icon: void 0 } : entry; | |
| 328 | + }) | |
| 329 | + }; | |
| 330 | + }) | |
| 331 | + ); | |
| 332 | + }); | |
| 333 | + } | |
| 334 | + } | |
| 179 | 335 | }); |
| 180 | 336 | return () => { |
| 181 | 337 | cancelled = true; |
| 182 | 338 | }; |
| @@ -182,10 +338,49 @@ | ||
| 182 | 338 | }; |
| 183 | 339 | }, [records]); |
| 184 | 340 | return [widgetTypes, isResolvingWidgetTypes]; |
| 185 | 341 | } |
| 342 | + | |
| 343 | +// packages/widget-primitives/build-module/widget-host/widget-host.mjs | |
| 344 | +var import_element3 = __toESM(require_element(), 1); | |
| 345 | +var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1); | |
| 346 | +var WidgetHostContext = (0, import_element3.createContext)({}); | |
| 347 | +function WidgetHostProvider({ | |
| 348 | + value, | |
| 349 | + children | |
| 350 | +}) { | |
| 351 | + const inherited = (0, import_element3.useContext)(WidgetHostContext); | |
| 352 | + const merged = (0, import_element3.useMemo)( | |
| 353 | + () => ({ ...inherited, ...value }), | |
| 354 | + [inherited, value] | |
| 355 | + ); | |
| 356 | + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(WidgetHostContext.Provider, { value: merged, children }); | |
| 357 | +} | |
| 358 | +function useWidgetHost() { | |
| 359 | + return (0, import_element3.useContext)(WidgetHostContext); | |
| 360 | +} | |
| 361 | + | |
| 362 | +// packages/widget-primitives/build-module/widget-host/host-link.mjs | |
| 363 | +var import_element4 = __toESM(require_element(), 1); | |
| 364 | +var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1); | |
| 365 | +var HostLink = (0, import_element4.forwardRef)( | |
| 366 | + function HostLink2({ href, children, ...props }, ref) { | |
| 367 | + const { links } = useWidgetHost(); | |
| 368 | + const { download, target } = props; | |
| 369 | + const opensNewDocument = download !== void 0 && download !== false || /^_blank$/i.test(target ?? ""); | |
| 370 | + const path = links && !opensNewDocument ? links.match(href) : null; | |
| 371 | + if (links && path !== null) { | |
| 372 | + return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(links.Link, { ref, path, ...props, children }); | |
| 373 | + } | |
| 374 | + return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("a", { ref, href, ...props, children }); | |
| 375 | + } | |
| 376 | +); | |
| 186 | 377 | export { |
| 378 | + HostLink, | |
| 379 | + WidgetHostProvider, | |
| 187 | 380 | WidgetRender, |
| 188 | 381 | registerFieldType, |
| 382 | + registerIconResolver, | |
| 383 | + useWidgetHost, | |
| 189 | 384 | useWidgetTypes |
| 190 | 385 | }; |
| 191 | 386 | //# sourceMappingURL=index.js.map |