1- import { EmptyContent } from "components/EmptyContent" ;
2- import { HelpText } from "components/HelpText" ;
3- import { Tabs } from "components/Tabs" ;
4- import {
5- clearMockWindow ,
6- clearStyleEval ,
7- ConstructorToComp ,
8- evalFunc ,
9- evalStyle ,
10- RecordConstructorToComp ,
11- } from "lowcoder-core" ;
12- import { CodeTextControl } from "comps/controls/codeTextControl" ;
13- import SimpleStringControl from "comps/controls/simpleStringControl" ;
14- import { MultiCompBuilder , withPropertyViewFn } from "comps/generators" ;
15- import { list } from "comps/generators/list" ;
16- import { BaseSection , CustomModal , PlusIcon , ScrollBar } from "lowcoder-design" ;
17- import React , { useContext , useEffect , useState } from "react" ;
18- import styled from "styled-components" ;
19- import { ExternalEditorContext } from "util/context/ExternalEditorContext" ;
20- import { runScriptInHost } from "util/commonUtils" ;
21- import { getGlobalSettings } from "comps/utils/globalSettings" ;
22- import { trans } from "i18n" ;
23- import log from "loglevel" ;
24- import { JSLibraryModal } from "components/JSLibraryModal" ;
25- import { JSLibraryTree } from "components/JSLibraryTree" ;
26- import { fetchJSLibrary } from "util/jsLibraryUtils" ;
27-
28- export interface ExternalPreload {
29- css ?: string ;
30- libs ?: string [ ] ;
31- script ?: string ;
32- runJavaScriptInHost ?: boolean ;
33- }
34-
35- interface RunAndClearable < T > {
36- run ( id : string , externalPreload ?: T ) : Promise < any > ;
37-
38- clear ( ) : Promise < any > ;
39- }
40-
41- class LibsCompBase extends list ( SimpleStringControl ) implements RunAndClearable < string [ ] > {
42- success : Record < string , boolean > = { } ;
43- globalVars : Record < string , string [ ] > = { } ;
44- externalLibs : string [ ] = [ ] ;
45- runInHost : boolean = false ;
46-
47- getAllLibs ( ) {
48- return this . externalLibs . concat ( this . getView ( ) . map ( ( i ) => i . getView ( ) ) ) ;
49- }
50-
51- async loadScript ( url : string ) {
52- if ( this . success [ url ] ) {
53- return ;
54- }
55- return fetchJSLibrary ( url ) . then ( ( code ) => {
56- evalFunc (
57- code ,
58- { } ,
59- { } ,
60- {
61- scope : "function" ,
62- disableLimit : this . runInHost ,
63- onSetGlobalVars : ( v : string ) => {
64- this . globalVars [ url ] = this . globalVars [ url ] || [ ] ;
65- if ( ! this . globalVars [ url ] . includes ( v ) ) {
66- this . globalVars [ url ] . push ( v ) ;
67- }
68- } ,
69- }
70- ) ;
71- this . success [ url ] = true ;
72- } ) ;
73- }
74-
75- async loadAllLibs ( ) {
76- const scriptRunners = this . getAllLibs ( ) . map ( ( url ) =>
77- this . loadScript ( url ) . catch ( ( e ) => {
78- log . warn ( e ) ;
79- } )
80- ) ;
81-
82- try {
83- await Promise . all ( scriptRunners ) ;
84- } catch ( e ) {
85- log . warn ( "load preload libs error:" , e ) ;
86- }
87- }
88-
89- async run ( id : string , externalLibs : string [ ] = [ ] , runInHost : boolean = false ) {
90- this . externalLibs = externalLibs ;
91- this . runInHost = runInHost ;
92- return this . loadAllLibs ( ) ;
93- }
94-
95- async clear ( ) : Promise < any > {
96- clearMockWindow ( ) ;
97- }
98- }
99-
100- const LibsComp = withPropertyViewFn ( LibsCompBase , ( comp ) => {
101- useEffect ( ( ) => {
102- comp . loadAllLibs ( ) ;
103- } , [ comp . getView ( ) . length ] ) ;
104- return (
105- < ScrollBar style = { { height : "295px" } } >
106- { comp . getAllLibs ( ) . length === 0 && (
107- < EmptyContent text = { trans ( "preLoad.jsLibraryEmptyContent" ) } style = { { margin : "0 16px" } } />
108- ) }
109- < JSLibraryTree
110- mode = { "column" }
111- libs = { comp
112- . getView ( )
113- . map ( ( i ) => ( {
114- url : i . getView ( ) ,
115- deletable : true ,
116- exportedAs : comp . globalVars [ i . getView ( ) ] ?. [ 0 ] ,
117- } ) )
118- . concat (
119- comp . externalLibs . map ( ( l ) => ( {
120- url : l ,
121- deletable : false ,
122- exportedAs : comp . globalVars [ l ] ?. [ 0 ] ,
123- } ) )
124- ) }
125- onDelete = { ( idx ) => {
126- comp . dispatch ( comp . deleteAction ( idx ) ) ;
127- } }
128- />
129- </ ScrollBar >
130- ) ;
131- } ) ;
132-
133- function runScript ( code : string , inHost ?: boolean ) {
134- if ( inHost ) {
135- runScriptInHost ( code ) ;
136- return ;
137- }
138- try {
139- evalFunc ( code , { } , { } ) ;
140- } catch ( e ) {
141- log . error ( e ) ;
142- }
143- }
144-
145- class ScriptComp extends CodeTextControl implements RunAndClearable < string > {
146- runInHost : boolean = false ;
147-
148- runPreloadScript ( ) {
149- const code = this . getView ( ) ;
150- if ( ! code ) {
151- return ;
152- }
153- runScript ( code , this . runInHost ) ;
154- }
155-
156- async run ( id : string , externalScript : string = "" , runInHost : boolean = false ) {
157- this . runInHost = runInHost ;
158- if ( externalScript ) {
159- runScript ( externalScript , runInHost ) ;
160- }
161- this . runPreloadScript ( ) ;
162- }
163-
164- async clear ( ) : Promise < any > {
165- clearMockWindow ( ) ;
166- }
167- }
168-
169- class CSSComp extends CodeTextControl implements RunAndClearable < string > {
170- id = "" ;
171- externalCSS : string = "" ;
172-
173- async applyAllCSS ( ) {
174- const css = this . getView ( ) ;
175- evalStyle ( this . id , [ this . externalCSS , css ] ) ;
176- }
177-
178- async run ( id : string , externalCSS : string = "" ) {
179- this . id = id ;
180- this . externalCSS = externalCSS ;
181- return this . applyAllCSS ( ) ;
182- }
183-
184- async clear ( ) {
185- clearStyleEval ( this . id ) ;
186- }
187- }
188-
189- class GlobalCSSComp extends CodeTextControl implements RunAndClearable < string > {
190- id = "" ;
191- externalCSS : string = "" ;
192-
193- async applyAllCSS ( ) {
194- const css = this . getView ( ) ;
195- evalStyle ( this . id , [ this . externalCSS , css ] , true ) ;
196- }
197-
198- async run ( id : string , externalCSS : string = "" ) {
199- this . id = id ;
200- this . externalCSS = externalCSS ;
201- return this . applyAllCSS ( ) ;
202- }
203-
204- async clear ( ) {
205- clearStyleEval ( this . id ) ;
206- }
207- }
208-
209- const childrenMap = {
210- libs : LibsComp ,
211- script : ScriptComp ,
212- css : CSSComp ,
213- globalCSS : GlobalCSSComp ,
214- } ;
215-
216- type ChildrenInstance = RecordConstructorToComp < typeof childrenMap > ;
217-
218- function JavaScriptTabPane ( props : { comp : ConstructorToComp < typeof ScriptComp > } ) {
219- useEffect ( ( ) => {
220- props . comp . runPreloadScript ( ) ;
221- } , [ props . comp ] ) ;
222-
223- const codePlaceholder = `window.name = 'Tom';\nwindow.greet = () => "hello world";` ;
224-
225- return (
226- < >
227- < HelpText style = { { marginBottom : 20 } } > { trans ( "preLoad.jsHelpText" ) } </ HelpText >
228- { props . comp . propertyView ( {
229- expandable : false ,
230- styleName : "window" ,
231- codeType : "Function" ,
232- language : "javascript" ,
233- placeholder : codePlaceholder ,
234- } ) }
235- </ >
236- ) ;
237- }
238-
239- function CSSTabPane ( props : { comp : CSSComp , isGlobal ?: boolean } ) {
240- useEffect ( ( ) => {
241- props . comp . applyAllCSS ( ) ;
242- } , [ props . comp ] ) ;
243-
244- const codePlaceholder = `.top-header {\n background-color: red; \n}` ;
245-
246- return (
247- < >
248- < HelpText style = { { marginBottom : 20 } } > { trans ( "preLoad.cssHelpText" ) } </ HelpText >
249- { props . comp . propertyView ( {
250- expandable : false ,
251- placeholder : codePlaceholder ,
252- styleName : "window" ,
253- language : "css" ,
254- } ) }
255- </ >
256- ) ;
257- }
258-
259- enum TabKey {
260- JavaScript = "js" ,
261- CSS = "css" ,
262- GLOBAL_CSS = "global_css" ,
263- }
264-
265- function PreloadConfigModal ( props : ChildrenInstance ) {
266- const [ activeKey , setActiveKey ] = useState ( TabKey . JavaScript ) ;
267- const { showScriptsAndStyleModal, changeExternalState } = useContext ( ExternalEditorContext ) ;
268-
269- const tabItems = [
270- {
271- key : TabKey . JavaScript ,
272- label : 'JavaScript' ,
273- children : < JavaScriptTabPane comp = { props . script } />
274- } ,
275- {
276- key : TabKey . CSS ,
277- label : 'CSS' ,
278- children : < CSSTabPane comp = { props . css } />
279- } ,
280- {
281- key : TabKey . GLOBAL_CSS ,
282- label : 'Global CSS' ,
283- children : < CSSTabPane comp = { props . globalCSS } isGlobal />
284- } ,
285- ]
286- return (
287- < CustomModal
288- draggable
289- mask = { activeKey !== TabKey . CSS }
290- open = { showScriptsAndStyleModal }
291- title = { trans ( "preLoad.scriptsAndStyles" ) }
292- destroyOnHidden
293- onCancel = { ( ) => changeExternalState ?.( { showScriptsAndStyleModal : false } ) }
294- showOkButton = { false }
295- showCancelButton = { false }
296- width = "600px"
297- >
298- < Tabs
299- onChange = { ( k ) => setActiveKey ( k as TabKey ) }
300- style = { { marginBottom : 8 , marginTop : 4 } }
301- activeKey = { activeKey }
302- items = { tabItems }
303- >
304- </ Tabs >
305- </ CustomModal >
306- ) ;
307- }
308-
309- const PreloadCompBase = new MultiCompBuilder ( childrenMap , ( ) => { } )
310- . setPropertyViewFn ( ( children ) => < PreloadConfigModal { ...children } /> )
311- . build ( ) ;
312-
313- const AddJSLibraryButton = styled . div `
314- cursor: pointer;
315- margin-right: 16px;
316-
317- g g {
318- stroke: #8b8fa3;
319- }
320-
321- &:hover {
322- g g {
323- stroke: #222222;
324- }
325- }
326- ` ;
327-
328- const JSLibraryWrapper = styled . div `
329- position: relative;
330- ` ;
331-
332- export class PreloadComp extends PreloadCompBase {
333- async clear ( ) {
334- return Promise . allSettled ( Object . values ( this . children ) . map ( ( i ) => i . clear ( ) ) ) ;
335- }
336-
337- async run ( id : string ) {
338- const { orgCommonSettings = { } } = getGlobalSettings ( ) ;
339- const { preloadCSS, preloadGlobalCSS, preloadJavaScript, preloadLibs, runJavaScriptInHost } = orgCommonSettings ;
340- await this . children . css . run ( id , preloadCSS || "" ) ;
341- await this . children . globalCSS . run ( 'body' , preloadGlobalCSS || "" ) ;
342- await this . children . libs . run ( id , preloadLibs || [ ] , ! ! runJavaScriptInHost ) ;
343- await this . children . script . run ( id , preloadJavaScript || "" , ! ! runJavaScriptInHost ) ;
344- }
345-
346- getJSLibraryPropertyView ( ) {
347- const libs = this . children . libs ;
348- return (
349- < JSLibraryWrapper >
350- < BaseSection
351- name = { trans ( "preLoad.jsLibrary" ) }
352- width = { 288 }
353- noMargin
354- style = { {
355- borderTop : "1px solid #e1e3eb" ,
356- backgroundColor : "#fff" ,
357- } }
358- additionalButton = {
359- < AddJSLibraryButton >
360- < JSLibraryModal
361- runInHost = { libs . runInHost }
362- trigger = { < PlusIcon height = { "46px" } /> }
363- onCheck = { ( url ) => ! libs . getAllLibs ( ) . includes ( url ) }
364- onLoad = { ( url ) => libs . loadScript ( url ) }
365- onSuccess = { ( url ) => libs . dispatch ( libs . pushAction ( url ) ) }
366- />
367- </ AddJSLibraryButton >
368- }
369- >
370- { this . children . libs . getPropertyView ( ) }
371- </ BaseSection >
372- </ JSLibraryWrapper >
373- ) ;
374- }
375- }
1+ export { PreloadComp } from "./preLoadComp/preLoadComp" ;
0 commit comments