feat(dashboard,admin-sdk,admin-shared,admin-vite-plugin): Add support for UI extensions (#7383)
* intial work * update lock * add routes and fix HMR of configs * cleanup * rm imports * rm debug from plugin * address feedback * address feedback
This commit is contained in:
committed by
GitHub
parent
521c252dee
commit
f1176a0673
@@ -0,0 +1,2 @@
|
||||
export * from "./types"
|
||||
export * from "./utils"
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { ComponentType } from "react"
|
||||
|
||||
import { InjectionZone } from "../widgets"
|
||||
|
||||
export type WidgetConfig = {
|
||||
zone: InjectionZone | InjectionZone[]
|
||||
}
|
||||
|
||||
export type RouteConfig = {
|
||||
label?: string
|
||||
icon?: ComponentType
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { RouteConfig, WidgetConfig } from "./types"
|
||||
|
||||
function createConfigHelper<TConfig extends Record<string, unknown>>(
|
||||
config: TConfig
|
||||
): TConfig {
|
||||
return {
|
||||
...config,
|
||||
/**
|
||||
* This property is required to allow the config to be exported,
|
||||
* while still allowing HMR to work correctly.
|
||||
*
|
||||
* It tricks Fast Refresh into thinking that the config is a React component,
|
||||
* which allows it to be updated without a full page reload.
|
||||
*/
|
||||
$$typeof: Symbol.for("react.memo"),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a widget configuration.
|
||||
*
|
||||
* @param config The widget configuration.
|
||||
* @returns The widget configuration.
|
||||
*/
|
||||
export function defineWidgetConfig(config: WidgetConfig) {
|
||||
return createConfigHelper(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a route configuration.
|
||||
*
|
||||
* @param config The route configuration.
|
||||
* @returns The route configuration.
|
||||
*/
|
||||
export function defineRouteConfig(config: RouteConfig) {
|
||||
return createConfigHelper(config)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export const ROUTE_IMPORTS = ["routes/pages", "routes/links"] as const
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./constants"
|
||||
export * from "./types"
|
||||
@@ -0,0 +1,3 @@
|
||||
import { ROUTE_IMPORTS } from "./constants"
|
||||
|
||||
export type RouteImport = (typeof ROUTE_IMPORTS)[number]
|
||||
@@ -0,0 +1,40 @@
|
||||
import { ROUTE_IMPORTS } from "../routes"
|
||||
import { INJECTION_ZONES } from "../widgets"
|
||||
import { getVirtualId, getWidgetImport, resolveVirtualId } from "./utils"
|
||||
|
||||
const VIRTUAL_WIDGET_MODULES = INJECTION_ZONES.map((zone) => {
|
||||
return getVirtualId(getWidgetImport(zone))
|
||||
})
|
||||
|
||||
const VIRTUAL_ROUTE_MODULES = ROUTE_IMPORTS.map((route) => {
|
||||
return getVirtualId(route)
|
||||
})
|
||||
|
||||
/**
|
||||
* All virtual modules that are used in the admin panel. Virtual modules are used
|
||||
* to inject custom widgets, routes and settings. A virtual module is imported using
|
||||
* a string that corresponds to the id of the virtual module.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import ProductDetailsBefore from "virtual:medusa/widgets/product/details/before"
|
||||
* ```
|
||||
*/
|
||||
export const VIRTUAL_MODULES = [
|
||||
...VIRTUAL_WIDGET_MODULES,
|
||||
...VIRTUAL_ROUTE_MODULES,
|
||||
]
|
||||
|
||||
/**
|
||||
* Reolved paths to all virtual widget modules.
|
||||
*/
|
||||
export const RESOLVED_WIDGET_MODULES = VIRTUAL_WIDGET_MODULES.map((id) => {
|
||||
return resolveVirtualId(id)
|
||||
})
|
||||
|
||||
/**
|
||||
* Reolved paths to all virtual route modules.
|
||||
*/
|
||||
export const RESOLVED_ROUTE_MODULES = VIRTUAL_ROUTE_MODULES.map((id) => {
|
||||
return resolveVirtualId(id)
|
||||
})
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./constants"
|
||||
export * from "./utils"
|
||||
@@ -0,0 +1,25 @@
|
||||
import { InjectionZone } from "../widgets"
|
||||
|
||||
const PREFIX = "virtual:medusa/"
|
||||
|
||||
export const getVirtualId = (name: string) => {
|
||||
return `${PREFIX}${name}`
|
||||
}
|
||||
|
||||
export const resolveVirtualId = (id: string) => {
|
||||
return `\0${id}`
|
||||
}
|
||||
|
||||
export const getWidgetImport = (zone: InjectionZone) => {
|
||||
return `widgets/${zone.replace(/\./g, "/")}`
|
||||
}
|
||||
|
||||
export const getWidgetZone = (resolvedId: string): InjectionZone => {
|
||||
const virtualPrefix = `\0${PREFIX}widgets/`
|
||||
|
||||
const zone = resolvedId
|
||||
.replace(virtualPrefix, "")
|
||||
.replace(/\//g, ".") as InjectionZone
|
||||
|
||||
return zone as InjectionZone
|
||||
}
|
||||
@@ -1,64 +1,101 @@
|
||||
export const injectionZones = [
|
||||
// Order injection zones
|
||||
const ORDER_INJECTION_ZONES = [
|
||||
"order.details.before",
|
||||
"order.details.after",
|
||||
"order.list.before",
|
||||
"order.list.after",
|
||||
// Draft order injection zones
|
||||
] as const
|
||||
|
||||
const DRAFT_ORDER_INJECTION_ZONES = [
|
||||
"draft_order.list.before",
|
||||
"draft_order.list.after",
|
||||
"draft_order.details.before",
|
||||
"draft_order.details.after",
|
||||
// Customer injection zones
|
||||
] as const
|
||||
|
||||
const CUSTOMER_INJECTION_ZONES = [
|
||||
"customer.details.before",
|
||||
"customer.details.after",
|
||||
"customer.list.before",
|
||||
"customer.list.after",
|
||||
// Customer group injection zones
|
||||
] as const
|
||||
|
||||
const CUSTOMER_GROUP_INJECTION_ZONES = [
|
||||
"customer_group.details.before",
|
||||
"customer_group.details.after",
|
||||
"customer_group.list.before",
|
||||
"customer_group.list.after",
|
||||
// Product injection zones
|
||||
] as const
|
||||
|
||||
const PRODUCT_INJECTION_ZONES = [
|
||||
"product.details.before",
|
||||
"product.details.after",
|
||||
"product.list.before",
|
||||
"product.list.after",
|
||||
"product.details.side.before",
|
||||
"product.details.side.after",
|
||||
// Product collection injection zones
|
||||
] as const
|
||||
|
||||
const PRODUCT_COLLECTION_INJECTION_ZONES = [
|
||||
"product_collection.details.before",
|
||||
"product_collection.details.after",
|
||||
"product_collection.list.before",
|
||||
"product_collection.list.after",
|
||||
// Product category injection zones
|
||||
] as const
|
||||
|
||||
const PRODUCT_CATEGORY_INJECTION_ZONES = [
|
||||
"product_category.details.before",
|
||||
"product_category.details.after",
|
||||
"product_category.list.before",
|
||||
"product_category.list.after",
|
||||
// Price list injection zones
|
||||
] as const
|
||||
|
||||
const PRICE_LIST_INJECTION_ZONES = [
|
||||
"price_list.details.before",
|
||||
"price_list.details.after",
|
||||
"price_list.list.before",
|
||||
"price_list.list.after",
|
||||
// Discount injection zones
|
||||
] as const
|
||||
|
||||
const DISCOUNT_INJECTION_ZONES = [
|
||||
"discount.details.before",
|
||||
"discount.details.after",
|
||||
"discount.list.before",
|
||||
"discount.list.after",
|
||||
// Promotion injection zones
|
||||
] as const
|
||||
|
||||
const PROMOTION_INJECTION_ZONES = [
|
||||
"promotion.details.before",
|
||||
"promotion.details.after",
|
||||
"promotion.list.before",
|
||||
"promotion.list.after",
|
||||
// Gift card injection zones
|
||||
] as const
|
||||
|
||||
const GIFT_CARD_INJECTION_ZONES = [
|
||||
"gift_card.details.before",
|
||||
"gift_card.details.after",
|
||||
"gift_card.list.before",
|
||||
"gift_card.list.after",
|
||||
"custom_gift_card.before",
|
||||
"custom_gift_card.after",
|
||||
// Login
|
||||
"login.before",
|
||||
"login.after",
|
||||
] as const
|
||||
|
||||
const LOGIN_INJECTION_ZONES = ["login.before", "login.after"] as const
|
||||
|
||||
/**
|
||||
* All valid injection zones in the admin panel. An injection zone is a specific place
|
||||
* in the admin panel where a plugin can inject custom widgets.
|
||||
*/
|
||||
export const INJECTION_ZONES = [
|
||||
...ORDER_INJECTION_ZONES,
|
||||
...DRAFT_ORDER_INJECTION_ZONES,
|
||||
...CUSTOMER_INJECTION_ZONES,
|
||||
...CUSTOMER_GROUP_INJECTION_ZONES,
|
||||
...PRODUCT_INJECTION_ZONES,
|
||||
...PRODUCT_COLLECTION_INJECTION_ZONES,
|
||||
...PRODUCT_CATEGORY_INJECTION_ZONES,
|
||||
...PRICE_LIST_INJECTION_ZONES,
|
||||
...DISCOUNT_INJECTION_ZONES,
|
||||
...PROMOTION_INJECTION_ZONES,
|
||||
...GIFT_CARD_INJECTION_ZONES,
|
||||
...LOGIN_INJECTION_ZONES,
|
||||
] as const
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./constants"
|
||||
export * from "./types"
|
||||
export * from "./utils"
|
||||
@@ -0,0 +1,3 @@
|
||||
import { INJECTION_ZONES } from "./constants"
|
||||
|
||||
export type InjectionZone = (typeof INJECTION_ZONES)[number]
|
||||
@@ -0,0 +1,9 @@
|
||||
import { INJECTION_ZONES } from "./constants"
|
||||
import { InjectionZone } from "./types"
|
||||
|
||||
/**
|
||||
* Validates that the provided zone is a valid injection zone for a widget.
|
||||
*/
|
||||
export function isValidInjectionZone(zone: any): zone is InjectionZone {
|
||||
return INJECTION_ZONES.includes(zone)
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from "./constants"
|
||||
export * from "./types"
|
||||
export * from "./extensions/config"
|
||||
export * from "./extensions/virtual"
|
||||
export * from "./extensions/widgets"
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import { injectionZones } from "./constants"
|
||||
|
||||
export type InjectionZone = (typeof injectionZones)[number]
|
||||
Reference in New Issue
Block a user