bsign-customization-full 0.0.4 → 0.0.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bsign-customization-full",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite",
package/src/index.tsx CHANGED
@@ -1,14 +1,16 @@
1
1
  import { createRoot, type Root } from "react-dom/client"
2
- import SignCustomizer, { type SignCustomizerProps } from "./AppDemo2"
2
+ import type { ComponentType } from "react"
3
+ import type { SignCustomizerProps } from "./AppDemo2"
3
4
  import resizableStyles from "react-resizable/css/styles.css?inline"
4
5
  import widgetStyles from "./index.css?inline"
5
6
  import { WidgetPortalContainerContext } from "./lib/widget-context"
6
- import { resolveAssetUrl } from "./lib/asset-url"
7
+ import { resolveAssetUrl, setAssetBaseUrl } from "./lib/asset-url"
7
8
 
8
9
  export type WidgetTarget = string | HTMLElement
9
10
 
10
11
  export interface MountConstructorWidgetProps extends SignCustomizerProps {
11
12
  widgetCss?: string | string[]
13
+ assetBaseUrl: string
12
14
  }
13
15
 
14
16
  const FONT_ASSET_PATHS = [
@@ -28,7 +30,20 @@ function resolveFontUrlsInCss(styles: string): string {
28
30
  }, styles)
29
31
  }
30
32
 
31
- const BASE_WIDGET_CSS = `${resizableStyles}\n${resolveFontUrlsInCss(widgetStyles)}`
33
+ function getBaseWidgetCss() {
34
+ return `${resizableStyles}\n${resolveFontUrlsInCss(widgetStyles)}`
35
+ }
36
+
37
+ let signCustomizerLoader: Promise<ComponentType<SignCustomizerProps>> | null = null
38
+
39
+ function loadSignCustomizer() {
40
+ if (!signCustomizerLoader) {
41
+ signCustomizerLoader = import("./AppDemo2").then((module) => module.default)
42
+ }
43
+
44
+ return signCustomizerLoader
45
+ }
46
+
32
47
  const rootsByHost = new WeakMap<HTMLElement, Root>()
33
48
 
34
49
  function resolveTarget(target: WidgetTarget): HTMLElement {
@@ -83,16 +98,18 @@ function ensurePortalNode(shadowRoot: ShadowRoot): HTMLElement {
83
98
  }
84
99
 
85
100
  function ensureBaseStyles(shadowRoot: ShadowRoot) {
101
+ const baseWidgetCss = getBaseWidgetCss()
86
102
  const existingStyles = shadowRoot.querySelector<HTMLStyleElement>(
87
103
  "style[data-bsign-widget-base-style]"
88
104
  )
89
105
  if (existingStyles) {
106
+ existingStyles.textContent = baseWidgetCss
90
107
  return
91
108
  }
92
109
 
93
110
  const styleTag = document.createElement("style")
94
111
  styleTag.setAttribute("data-bsign-widget-base-style", "")
95
- styleTag.textContent = BASE_WIDGET_CSS
112
+ styleTag.textContent = baseWidgetCss
96
113
  shadowRoot.prepend(styleTag)
97
114
  }
98
115
 
@@ -134,9 +151,17 @@ function syncCustomStyles(shadowRoot: ShadowRoot, widgetCss?: string | string[])
134
151
 
135
152
  export function mountConstructorWidget(
136
153
  target: WidgetTarget,
137
- props: MountConstructorWidgetProps = {}
154
+ props: MountConstructorWidgetProps
138
155
  ) {
139
- const { widgetCss, ...widgetProps } = props
156
+ const { widgetCss, assetBaseUrl, ...widgetProps } = props
157
+ if (!assetBaseUrl || typeof assetBaseUrl !== "string") {
158
+ throw new Error(
159
+ "mountConstructorWidget: `assetBaseUrl` prop is required, for example `https://cdn.example.com/widget/dist/`"
160
+ )
161
+ }
162
+
163
+ setAssetBaseUrl(assetBaseUrl)
164
+
140
165
  const host = resolveTarget(target)
141
166
  const shadowRoot = ensureShadowRoot(host)
142
167
  const mountNode = ensureMountNode(shadowRoot)
@@ -151,9 +176,15 @@ export function mountConstructorWidget(
151
176
  rootsByHost.set(host, root)
152
177
  }
153
178
 
154
- root.render(
155
- <WidgetPortalContainerContext.Provider value={portalNode}>
156
- <SignCustomizer {...widgetProps} />
157
- </WidgetPortalContainerContext.Provider>
158
- )
179
+ const widgetRoot = root
180
+
181
+ void loadSignCustomizer().then((SignCustomizer) => {
182
+ widgetRoot.render(
183
+ <WidgetPortalContainerContext.Provider value={portalNode}>
184
+ <SignCustomizer {...widgetProps} />
185
+ </WidgetPortalContainerContext.Provider>
186
+ )
187
+ }).catch((error) => {
188
+ console.error("mountConstructorWidget: failed to load widget module", error)
189
+ })
159
190
  }
@@ -2,130 +2,37 @@ const ABSOLUTE_URL_PATTERN = /^(?:[a-z][a-z\d+\-.]*:)?\/\//i
2
2
  const SPECIAL_PROTOCOL_PATTERN = /^(?:data|blob):/i
3
3
 
4
4
  /**
5
- * Default CDN source for static widget assets (templates/colors/fonts/logo/etc).
6
- * Can be overridden globally before widget init:
7
- * window.BSIGN_WIDGET_ASSET_BASE_URL = "https://your-cdn/path/to/dist/"
5
+ * Static assets base URL (templates/colors/fonts/logo/etc).
6
+ * Must be set explicitly from widget mount props:
7
+ * mountConstructorWidget(..., { assetBaseUrl: "https://your-cdn/path/to/dist/" })
8
8
  */
9
- const DEFAULT_CDN_ASSET_BASE_URL =
10
- "https://cdn.jsdelivr.net/npm/bsign-customization-full@latest/dist/"
11
-
12
- type WindowWithWidgetAssetBase = Window & {
13
- BSIGN_WIDGET_ASSET_BASE_URL?: string
14
- }
9
+ const DEFAULT_ASSET_BASE_URL = "/"
15
10
 
16
11
  function toBaseUrl(url: string): string | undefined {
17
12
  try {
18
- return new URL(".", url).toString()
19
- } catch {
20
- return undefined
21
- }
22
- }
23
-
24
- function isLocalhostHost(hostname: string): boolean {
25
- const normalizedHost = hostname.toLowerCase()
26
- return (
27
- normalizedHost === "localhost" ||
28
- normalizedHost === "127.0.0.1" ||
29
- normalizedHost === "[::1]" ||
30
- normalizedHost.endsWith(".localhost")
31
- )
32
- }
33
-
34
- function shouldUseRelativeUrls(): boolean {
35
- if (typeof window === "undefined") {
36
- return false
37
- }
38
-
39
- return isLocalhostHost(window.location.hostname)
40
- }
41
-
42
- function resolveGlobalAssetBaseUrl(): string | undefined {
43
- if (typeof window === "undefined") {
44
- return undefined
45
- }
46
-
47
- const candidate = (window as WindowWithWidgetAssetBase).BSIGN_WIDGET_ASSET_BASE_URL
48
- if (typeof candidate !== "string") {
49
- return undefined
50
- }
51
-
52
- const trimmed = candidate.trim()
53
- if (!trimmed) {
54
- return undefined
55
- }
56
-
57
- return toBaseUrl(trimmed) ?? trimmed
58
- }
59
-
60
- function resolveScriptTagAssetBaseUrl(): string | undefined {
61
- if (typeof document === "undefined") {
62
- return undefined
63
- }
64
-
65
- const currentScript = document.currentScript
66
- if (
67
- currentScript instanceof HTMLScriptElement &&
68
- typeof currentScript.src === "string" &&
69
- currentScript.src.includes("sign-constructor")
70
- ) {
71
- return toBaseUrl(currentScript.src)
72
- }
73
-
74
- const scripts = document.getElementsByTagName("script")
75
- for (let index = scripts.length - 1; index >= 0; index -= 1) {
76
- const script = scripts.item(index)
77
- if (
78
- script instanceof HTMLScriptElement &&
79
- typeof script.src === "string" &&
80
- script.src.includes("sign-constructor")
81
- ) {
82
- const base = toBaseUrl(script.src)
83
- if (base) {
84
- return base
85
- }
13
+ if (typeof window !== "undefined") {
14
+ return new URL(".", new URL(url, window.location.href)).toString()
86
15
  }
87
- }
88
-
89
- return undefined
90
- }
91
16
 
92
- function resolveImportMetaAssetBaseUrl(): string | undefined {
93
- const moduleUrl = import.meta.url
94
- if (!moduleUrl || !moduleUrl.includes("sign-constructor")) {
17
+ return new URL(".", url).toString()
18
+ } catch {
95
19
  return undefined
96
20
  }
97
-
98
- return toBaseUrl(moduleUrl)
99
21
  }
100
22
 
101
- function resolveDefaultAssetBaseUrl(): string {
102
- const globalBase = resolveGlobalAssetBaseUrl()
103
- if (globalBase) {
104
- return globalBase
105
- }
23
+ let assetBaseUrl = DEFAULT_ASSET_BASE_URL
106
24
 
107
- const scriptBase = resolveScriptTagAssetBaseUrl()
108
- if (scriptBase) {
109
- return scriptBase
25
+ export function setAssetBaseUrl(baseUrl: string) {
26
+ if (typeof baseUrl !== "string" || baseUrl.trim() === "") {
27
+ throw new Error("setAssetBaseUrl: base URL is required")
110
28
  }
111
29
 
112
- const importMetaBase = resolveImportMetaAssetBaseUrl()
113
- if (importMetaBase) {
114
- return importMetaBase
30
+ const resolvedBaseUrl = toBaseUrl(baseUrl)
31
+ if (!resolvedBaseUrl) {
32
+ throw new Error(`setAssetBaseUrl: invalid base URL "${baseUrl}"`)
115
33
  }
116
34
 
117
- return DEFAULT_CDN_ASSET_BASE_URL
118
- }
119
-
120
- let assetBaseUrl = resolveDefaultAssetBaseUrl()
121
-
122
- export function setAssetBaseUrl(baseUrl?: string) {
123
- if (!baseUrl) {
124
- assetBaseUrl = resolveDefaultAssetBaseUrl()
125
- return
126
- }
127
-
128
- assetBaseUrl = toBaseUrl(baseUrl) ?? assetBaseUrl
35
+ assetBaseUrl = resolvedBaseUrl
129
36
  }
130
37
 
131
38
  export function getAssetBaseUrl() {
@@ -141,15 +48,6 @@ export function resolveAssetUrl(path: string): string {
141
48
  return path
142
49
  }
143
50
 
144
- if (shouldUseRelativeUrls()) {
145
- if (path.startsWith("./") || path.startsWith("../")) {
146
- return path
147
- }
148
-
149
- const normalizedPath = path.replace(/^\/+/, "")
150
- return `./${normalizedPath}`
151
- }
152
-
153
- const normalizedPath = path.startsWith("/") ? path.slice(1) : path
51
+ const normalizedPath = path.replace(/^\/+/, "")
154
52
  return new URL(normalizedPath, getAssetBaseUrl()).toString()
155
53
  }
package/test/preview.html CHANGED
@@ -11,10 +11,11 @@
11
11
 
12
12
  <div id="root"></div>
13
13
 
14
- <script src="https://cdn.jsdelivr.net/npm/bsign-customization@latest/dist/sign-constructor.iife.js"></script>
14
+ <script src="https://cdn.jsdelivr.net/npm/bsign-customization-full@latest/dist/sign-constructor.iife.js"></script>
15
15
 
16
16
  <script>
17
17
  AppDemo2.mountConstructorWidget('#root', {
18
+ assetBaseUrl: "https://cdn.jsdelivr.net/npm/bsign-customization-full@latest/dist/",
18
19
  openButton: {
19
20
  label: "Open constructor",
20
21
  className: "open-constructor-button",