@huaqiu/dsh-auth 0.4.13 → 0.5.2
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/lib/client.js +767 -34
- package/lib/client.js.map +1 -1
- package/package.json +2 -2
- package/src/client/client.ts +63 -0
- package/src/client/i18n.ts +37 -0
- package/src/client/index.tsx +15 -0
- package/src/client/lib.ts +111 -0
- package/src/client/ui/sidebar-action.tsx +477 -20
- package/src/client/ui/subscription-dialog.ts +183 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The subscription dialog — a real modal (backdrop + centered card + iframe)
|
|
3
|
+
* for `gen.eda.cn/subscription`, mirroring the login dialog's DOM pattern
|
|
4
|
+
* (`ui/login-dialog.ts`): a fixed full-viewport semi-transparent backdrop +
|
|
5
|
+
* centered host-surface card + the subscription iframe.
|
|
6
|
+
*
|
|
7
|
+
* WHY A CARD AND NOT A FULL-VIEWPORT IFRAME
|
|
8
|
+
*
|
|
9
|
+
* Same reason as the login dialog: the iframe element sits on top of the
|
|
10
|
+
* host page, and Blink paints a WHITE base canvas behind any transparent
|
|
11
|
+
* region of the embedded doc. Putting the iframe inside a host-painted card
|
|
12
|
+
* masks that white in both light and dark themes (the card surface shows
|
|
13
|
+
* through wherever the embedded doc is transparent).
|
|
14
|
+
*
|
|
15
|
+
* SIZING: the subscription page prefers 1400×740 (`SUBSCRIPTION_PREFERRED_SIZE`,
|
|
16
|
+
* shared with the host-side native dialog via DialogService.OpenUrl). The card
|
|
17
|
+
* clamps to the viewport (`min(96vw, width)` × `min(92vh, height)`) so it can
|
|
18
|
+
* never overflow a small host window; the iframe fills the card.
|
|
19
|
+
*
|
|
20
|
+
* Close paths: backdrop click, the × button, and Escape. Unlike the login
|
|
21
|
+
* embed there is no postMessage close contract to honor — the page is
|
|
22
|
+
* `gen.eda.cn/subscription`, not `auth.eda.cn`.
|
|
23
|
+
*/
|
|
24
|
+
import { SUBSCRIPTION_PREFERRED_SIZE } from '../lib.js'
|
|
25
|
+
import { translate } from '../i18n.js'
|
|
26
|
+
import { getCurrentLocale, getCurrentSurfaceColor, subscribeUiEnv, syncUiEnv } from '../ui-env.js'
|
|
27
|
+
|
|
28
|
+
/** Aria / data attribute names — stable so tests and CSS can target them. */
|
|
29
|
+
export const SUB_DIALOG_OVERLAY_ATTR = 'data-hq-sub-dialog'
|
|
30
|
+
export const SUB_DIALOG_CARD_ATTR = 'data-hq-sub-dialog-card'
|
|
31
|
+
export const SUB_DIALOG_IFRAME_ATTR = 'data-hq-sub-dialog-iframe'
|
|
32
|
+
export const SUB_DIALOG_CLOSE_ATTR = 'data-hq-sub-dialog-close'
|
|
33
|
+
|
|
34
|
+
let container: HTMLDivElement | null = null
|
|
35
|
+
let unsubscribe: (() => void) | null = null
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Open the subscription dialog. Idempotent: a second call while open is a
|
|
39
|
+
* no-op. `size` overrides the default 1400×740 preferred size (clamped to the
|
|
40
|
+
* viewport).
|
|
41
|
+
*/
|
|
42
|
+
export function openSubscriptionDialog(
|
|
43
|
+
url: string,
|
|
44
|
+
options: { size?: { width?: number; height?: number } } = {},
|
|
45
|
+
onClose?: () => void,
|
|
46
|
+
): void {
|
|
47
|
+
if (container) return
|
|
48
|
+
syncUiEnv()
|
|
49
|
+
const locale = getCurrentLocale()
|
|
50
|
+
|
|
51
|
+
const width = Math.max(320, options.size?.width ?? SUBSCRIPTION_PREFERRED_SIZE.width)
|
|
52
|
+
const height = Math.max(240, options.size?.height ?? SUBSCRIPTION_PREFERRED_SIZE.height)
|
|
53
|
+
|
|
54
|
+
const root = document.createElement('div')
|
|
55
|
+
root.setAttribute(SUB_DIALOG_OVERLAY_ATTR, '')
|
|
56
|
+
root.style.cssText = [
|
|
57
|
+
'position:fixed',
|
|
58
|
+
'inset:0',
|
|
59
|
+
'width:100vw',
|
|
60
|
+
'height:100vh',
|
|
61
|
+
'border:0',
|
|
62
|
+
'z-index:2147483646',
|
|
63
|
+
'background:rgba(0, 0, 0, 0.55)',
|
|
64
|
+
'display:flex',
|
|
65
|
+
'align-items:center',
|
|
66
|
+
'justify-content:center',
|
|
67
|
+
'box-sizing:border-box',
|
|
68
|
+
].join(';')
|
|
69
|
+
|
|
70
|
+
const card = document.createElement('div')
|
|
71
|
+
card.setAttribute(SUB_DIALOG_CARD_ATTR, '')
|
|
72
|
+
const applyCardColors = (): void => {
|
|
73
|
+
card.style.cssText = [
|
|
74
|
+
`width:min(96vw, ${width}px)`,
|
|
75
|
+
`height:min(92vh, ${height}px)`,
|
|
76
|
+
'border-radius:12px',
|
|
77
|
+
'box-shadow:0 24px 48px rgba(0, 0, 0, 0.32)',
|
|
78
|
+
'position:relative',
|
|
79
|
+
'box-sizing:border-box',
|
|
80
|
+
`background:${getCurrentSurfaceColor()}`,
|
|
81
|
+
'display:flex',
|
|
82
|
+
'flex-direction:column',
|
|
83
|
+
'overflow:hidden',
|
|
84
|
+
].join(';')
|
|
85
|
+
}
|
|
86
|
+
applyCardColors()
|
|
87
|
+
|
|
88
|
+
const closeButton = document.createElement('button')
|
|
89
|
+
closeButton.setAttribute(SUB_DIALOG_CLOSE_ATTR, '')
|
|
90
|
+
closeButton.type = 'button'
|
|
91
|
+
closeButton.setAttribute('aria-label', translate(locale, 'dialog.close'))
|
|
92
|
+
closeButton.title = translate(locale, 'dialog.close')
|
|
93
|
+
closeButton.textContent = '×'
|
|
94
|
+
closeButton.style.cssText = [
|
|
95
|
+
'position:absolute',
|
|
96
|
+
'top:6px',
|
|
97
|
+
'right:10px',
|
|
98
|
+
'width:28px',
|
|
99
|
+
'height:28px',
|
|
100
|
+
'border:0',
|
|
101
|
+
'background:transparent',
|
|
102
|
+
'color:var(--dsw-alias-label-secondary, #5b6472)',
|
|
103
|
+
'font-size:22px',
|
|
104
|
+
'line-height:1',
|
|
105
|
+
'cursor:pointer',
|
|
106
|
+
'border-radius:6px',
|
|
107
|
+
'padding:0',
|
|
108
|
+
'z-index:1',
|
|
109
|
+
].join(';')
|
|
110
|
+
closeButton.addEventListener('click', closeSubscriptionDialog)
|
|
111
|
+
|
|
112
|
+
const iframe = document.createElement('iframe')
|
|
113
|
+
iframe.setAttribute(SUB_DIALOG_IFRAME_ATTR, '')
|
|
114
|
+
iframe.src = url
|
|
115
|
+
iframe.title = translate(locale, 'subscription.dialogTitle')
|
|
116
|
+
iframe.allow = 'clipboard-write'
|
|
117
|
+
iframe.style.cssText = [
|
|
118
|
+
'width:100%',
|
|
119
|
+
'height:100%',
|
|
120
|
+
'border:0',
|
|
121
|
+
'border-radius:8px',
|
|
122
|
+
`background:${getCurrentSurfaceColor()}`,
|
|
123
|
+
'display:block',
|
|
124
|
+
'flex:1 1 auto',
|
|
125
|
+
].join(';')
|
|
126
|
+
|
|
127
|
+
card.appendChild(closeButton)
|
|
128
|
+
card.appendChild(iframe)
|
|
129
|
+
root.appendChild(card)
|
|
130
|
+
document.body.appendChild(root)
|
|
131
|
+
|
|
132
|
+
root.addEventListener('mousedown', backdropMouseDown)
|
|
133
|
+
card.addEventListener('mousedown', stopPropagation)
|
|
134
|
+
document.addEventListener('keydown', onKeyDown)
|
|
135
|
+
|
|
136
|
+
// Track theme/locale flips so the card surface + iframe background follow
|
|
137
|
+
// the host while the dialog is open.
|
|
138
|
+
unsubscribe = subscribeUiEnv(() => {
|
|
139
|
+
if (!container) return
|
|
140
|
+
applyCardColors()
|
|
141
|
+
iframe.style.background = getCurrentSurfaceColor()
|
|
142
|
+
closeButton.title = translate(getCurrentLocale(), 'dialog.close')
|
|
143
|
+
closeButton.setAttribute('aria-label', closeButton.title)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
container = root
|
|
147
|
+
onCloseCallback = onClose ?? null
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Programmatic close (used by the auth client / tests). */
|
|
151
|
+
export function closeSubscriptionDialog(): void {
|
|
152
|
+
if (!container) return
|
|
153
|
+
container.remove()
|
|
154
|
+
container = null
|
|
155
|
+
document.removeEventListener('keydown', onKeyDown)
|
|
156
|
+
unsubscribe?.()
|
|
157
|
+
unsubscribe = null
|
|
158
|
+
const cb = onCloseCallback
|
|
159
|
+
onCloseCallback = null
|
|
160
|
+
cb?.()
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** True while the subscription dialog is mounted. */
|
|
164
|
+
export function isSubscriptionDialogOpen(): boolean {
|
|
165
|
+
return container !== null
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let onCloseCallback: (() => void) | null = null
|
|
169
|
+
|
|
170
|
+
function backdropMouseDown(event: MouseEvent): void {
|
|
171
|
+
if (event.target === container) closeSubscriptionDialog()
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function stopPropagation(event: MouseEvent): void {
|
|
175
|
+
event.stopPropagation()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function onKeyDown(event: KeyboardEvent): void {
|
|
179
|
+
if (event.key === 'Escape') {
|
|
180
|
+
event.stopPropagation()
|
|
181
|
+
closeSubscriptionDialog()
|
|
182
|
+
}
|
|
183
|
+
}
|