pinstripe 0.23.0 → 0.24.1
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/app.js +35 -0
- package/lib/apps/_file_importer.js +1 -0
- package/lib/apps/docs.js +6 -0
- package/lib/apps/main.js +6 -0
- package/lib/command.js +3 -1
- package/lib/commands/generate_app.js +41 -0
- package/lib/commands/generate_component.js +22 -8
- package/lib/commands/list_apps.js +15 -0
- package/lib/commands/list_views.js +25 -2
- package/lib/commands/start_server.js +26 -4
- package/lib/component.js +48 -28
- package/lib/components/a.js +12 -6
- package/lib/components/document.js +17 -8
- package/lib/components/helpers.js +11 -14
- package/lib/components/pinstripe_frame.js +23 -10
- package/lib/components/pinstripe_markdown_editor.js +3 -4
- package/lib/components/pinstripe_modal.js +77 -0
- package/lib/components/pinstripe_overlay.js +9 -14
- package/lib/components/pinstripe_skeleton.js +55 -0
- package/lib/components/script.js +11 -0
- package/lib/database/client.js +37 -6
- package/lib/database/constants.js +1 -1
- package/lib/database/row.js +3 -5
- package/lib/index.js +1 -0
- package/lib/lru_cache.js +53 -0
- package/lib/lru_cache.test.js +45 -0
- package/lib/markdown.js +58 -0
- package/lib/project.js +8 -7
- package/lib/registry.js +7 -10
- package/lib/services/app.js +11 -0
- package/lib/services/client_builder.js +2 -0
- package/lib/services/config.js +7 -4
- package/lib/services/fetch.js +1 -2
- package/lib/services/render_form.js +58 -54
- package/lib/services/render_markdown.js +2 -36
- package/lib/services/render_view.js +1 -3
- package/lib/services/send_mail.js +1 -0
- package/lib/services/server.js +41 -26
- package/lib/services/view.js +6 -0
- package/lib/view.js +39 -3
- package/lib/views/{assets → main/assets}/stylesheets/all.css.js +1 -1
- package/package.json +4 -4
- package/lib/extensions/multi-app/index.js +0 -4
- package/lib/extensions/multi-app/views/_file_importer.js +0 -2
- package/lib/extensions/multi-app/views/apps/default.js +0 -6
- package/lib/extensions/multi-app/views/apps/guard.js +0 -7
- package/lib/extensions/multi-app/views/guard.js +0 -21
- package/lib/services/view_names.js +0 -8
- package/lib/view_file_importers/ejs.js +0 -82
- package/lib/views/assets/stylesheets/components/modal.css +0 -62
- /package/lib/views/{assets → main/assets}/javascripts/all.js.js +0 -0
- /package/lib/views/{assets → main/assets}/javascripts/all.js.map.js +0 -0
- /package/lib/views/{assets → main/assets}/stylesheets/components/button.css +0 -0
- /package/lib/views/{assets → main/assets}/stylesheets/components/card.css +0 -0
- /package/lib/views/{assets → main/assets}/stylesheets/components/form.css +0 -0
- /package/lib/views/{assets → main/assets}/stylesheets/components/frame.css +0 -0
- /package/lib/views/{assets → main/assets}/stylesheets/components/input.css +0 -0
- /package/lib/views/{assets → main/assets}/stylesheets/components/label.css +0 -0
- /package/lib/views/{assets → main/assets}/stylesheets/components/overlay.css +0 -0
- /package/lib/views/{assets → main/assets}/stylesheets/components/pagination.css +0 -0
- /package/lib/views/{assets → main/assets}/stylesheets/components/progress_bar.css +0 -0
- /package/lib/views/{assets → main/assets}/stylesheets/components/textarea.css +0 -0
- /package/lib/views/{assets → main/assets}/stylesheets/global.css +0 -0
- /package/lib/views/{assets → main/assets}/stylesheets/reset.css +0 -0
- /package/lib/views/{assets → main/assets}/stylesheets/vars.css +0 -0
package/lib/app.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
import { Class } from './class.js';
|
|
3
|
+
import { Registry } from './registry.js';
|
|
4
|
+
import { View } from './view.js';
|
|
5
|
+
import { ServiceConsumer } from './service_consumer.js';
|
|
6
|
+
|
|
7
|
+
export const App = Class.extend().include({
|
|
8
|
+
meta(){
|
|
9
|
+
this.include(Registry);
|
|
10
|
+
this.include(ServiceConsumer);
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
compose(){
|
|
14
|
+
return [];
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
get viewMapper(){
|
|
18
|
+
if(!this._viewMapper){
|
|
19
|
+
this._viewMapper = View.mapperFor(this.compose());
|
|
20
|
+
}
|
|
21
|
+
return this._viewMapper;
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
renderView(...args){
|
|
25
|
+
return this.viewMapper.renderView(this.context, ...args);
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
isView(...args){
|
|
29
|
+
return this.viewMapper.isView(...args);
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
get viewNames(){
|
|
33
|
+
return this.viewMapper.viewNames;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { App as default } from 'pinstripe';
|
package/lib/apps/docs.js
ADDED
package/lib/apps/main.js
ADDED
package/lib/command.js
CHANGED
|
@@ -10,7 +10,9 @@ export const Command = Class.extend().include({
|
|
|
10
10
|
this.include(ServiceConsumer);
|
|
11
11
|
|
|
12
12
|
this.assignProps({
|
|
13
|
-
normalizeName
|
|
13
|
+
normalizeName(name){
|
|
14
|
+
return inflector.dasherize(name);
|
|
15
|
+
},
|
|
14
16
|
|
|
15
17
|
get schedules(){
|
|
16
18
|
if(!this.hasOwnProperty('_schedules')){
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
async run(){
|
|
5
|
+
const [ name = '' ] = this.args;
|
|
6
|
+
const normalizedName = this.inflector.snakeify(name);
|
|
7
|
+
if(normalizedName == ''){
|
|
8
|
+
console.error('An app name must be given.');
|
|
9
|
+
process.exit();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { inProjectRootDir, generateFile, line, indent } = this.fsBuilder;
|
|
13
|
+
|
|
14
|
+
await inProjectRootDir(async () => {
|
|
15
|
+
|
|
16
|
+
await generateFile(`lib/apps/_file_importer.js`, { skipIfExists: true }, () => {
|
|
17
|
+
line();
|
|
18
|
+
line(`export { App as default } from 'pinstripe';`);
|
|
19
|
+
line();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
await generateFile(`lib/apps/${normalizedName}.js`, () => {
|
|
23
|
+
line();
|
|
24
|
+
line(`export default {`);
|
|
25
|
+
indent(() => {
|
|
26
|
+
line('compose(){');
|
|
27
|
+
indent(() => {
|
|
28
|
+
line(`return ['shared', '${this.inflector.dasherize(normalizedName)}'];`);
|
|
29
|
+
});
|
|
30
|
+
line('}');
|
|
31
|
+
});
|
|
32
|
+
line('};');
|
|
33
|
+
line();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await this.runCommand('generate-view', `${normalizedName}/index`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
export default {
|
|
3
3
|
async run(){
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
if(
|
|
7
|
-
console.error('A
|
|
4
|
+
const { extractArg } = this.cliUtils;
|
|
5
|
+
const name = this.inflector.snakeify(extractArg(''));
|
|
6
|
+
if(name == ''){
|
|
7
|
+
console.error('A component name must be given.');
|
|
8
8
|
process.exit();
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -17,21 +17,35 @@ export default {
|
|
|
17
17
|
line(`export { Component as default } from 'pinstripe';`);
|
|
18
18
|
line();
|
|
19
19
|
});
|
|
20
|
-
|
|
21
|
-
await generateFile(`lib/components/${
|
|
20
|
+
|
|
21
|
+
await generateFile(`lib/components/${name}.js`, () => {
|
|
22
22
|
line();
|
|
23
23
|
line(`export default {`);
|
|
24
24
|
indent(() => {
|
|
25
|
-
line(`initialize(){`);
|
|
25
|
+
line(`initialize(...args){`);
|
|
26
26
|
indent(() => {
|
|
27
27
|
line(`this.constructor.parent.prototype.initialize.call(this, ...args);`);
|
|
28
|
+
line();
|
|
29
|
+
line('this.shadow.patch(`');
|
|
30
|
+
indent(() => {
|
|
31
|
+
line(`<style>`);
|
|
32
|
+
indent(() => {
|
|
33
|
+
line(`.root {`);
|
|
34
|
+
indent(() => {
|
|
35
|
+
line(`background: yellow;`)
|
|
36
|
+
})
|
|
37
|
+
line(`}`);
|
|
38
|
+
});
|
|
39
|
+
line(`</style>`);
|
|
40
|
+
line(`<div class="root"><slot></div>`);
|
|
41
|
+
});
|
|
42
|
+
line('`);');
|
|
28
43
|
});
|
|
29
44
|
line(`}`);
|
|
30
45
|
});
|
|
31
46
|
line('};');
|
|
32
47
|
line();
|
|
33
48
|
});
|
|
34
|
-
|
|
35
49
|
});
|
|
36
50
|
}
|
|
37
51
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { App } from 'pinstripe';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
run(){
|
|
7
|
+
console.log('');
|
|
8
|
+
console.log('The following apps are available:');
|
|
9
|
+
console.log('');
|
|
10
|
+
App.names.forEach(appName => {
|
|
11
|
+
console.log(` * ${chalk.green(appName)} (composed of ${JSON.stringify(App.create(appName, this.context).compose())} views)`);
|
|
12
|
+
});
|
|
13
|
+
console.log('');
|
|
14
|
+
}
|
|
15
|
+
};
|
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import { View } from 'pinstripe';
|
|
3
|
+
import { App, View } from 'pinstripe';
|
|
4
4
|
|
|
5
5
|
export default {
|
|
6
6
|
run(){
|
|
7
|
+
const { extractOptions } = this.cliUtils;
|
|
8
|
+
|
|
9
|
+
const { app } = extractOptions();
|
|
10
|
+
|
|
11
|
+
if(app){
|
|
12
|
+
this.listComposedViews(typeof app == 'string' ? app : 'main');
|
|
13
|
+
} else {
|
|
14
|
+
this.listAllViews();
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
listComposedViews(appName){
|
|
19
|
+
const { viewNames, resolveView } = View.mapperFor(App.create(appName, this.context).compose());
|
|
20
|
+
console.log('');
|
|
21
|
+
console.log(`The following views have been composed for app "${appName}":`);
|
|
22
|
+
console.log('');
|
|
23
|
+
viewNames.forEach(viewName => {
|
|
24
|
+
console.log(` * ${chalk.green(viewName)} -> ${chalk.green(resolveView(viewName))}`);
|
|
25
|
+
});
|
|
26
|
+
console.log('');
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
listAllViews(){
|
|
7
30
|
console.log('');
|
|
8
|
-
console.log(
|
|
31
|
+
console.log(`The following views are available:`);
|
|
9
32
|
console.log('');
|
|
10
33
|
View.names.forEach(viewName => {
|
|
11
34
|
console.log(` * ${chalk.green(viewName)}`);
|
|
@@ -1,9 +1,31 @@
|
|
|
1
1
|
|
|
2
2
|
export default {
|
|
3
3
|
run(){
|
|
4
|
-
this.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const { extractOptions } = this.cliUtils;
|
|
5
|
+
|
|
6
|
+
const { app, withoutBot } = extractOptions({
|
|
7
|
+
app: `main:${process.env.HOST || '127.0.0.1'}:${parseInt(process.env.PORT || '3000')}`,
|
|
8
|
+
withoutBot: false
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const apps = [];
|
|
13
|
+
let currentPort = 3000;
|
|
14
|
+
|
|
15
|
+
app.trim().split(/\s+/).forEach((app) => {
|
|
16
|
+
const [name, ...serverConfig] = app.split(/:/);
|
|
17
|
+
const [ port, host = '127.0.0.1'] = serverConfig.reverse();
|
|
18
|
+
|
|
19
|
+
if(port){
|
|
20
|
+
apps.push({ name, port: parseInt(port), host });
|
|
21
|
+
} else {
|
|
22
|
+
while(apps.filter(app => app.host == host).map(({ port }) => port).includes(currentPort)) currentPort++;
|
|
23
|
+
apps.push({ name, port: currentPort, host });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
this.server.start(apps);
|
|
28
|
+
|
|
29
|
+
// if(!withoutBot) this.bot.start();
|
|
8
30
|
}
|
|
9
31
|
};
|
package/lib/component.js
CHANGED
|
@@ -13,8 +13,6 @@ export const Component = Class.extend().include({
|
|
|
13
13
|
meta(){
|
|
14
14
|
this.include(Registry);
|
|
15
15
|
|
|
16
|
-
const { importFile } = this;
|
|
17
|
-
|
|
18
16
|
this.assignProps({
|
|
19
17
|
instanceFor(node){
|
|
20
18
|
if(!node._component){
|
|
@@ -30,24 +28,32 @@ export const Component = Class.extend().include({
|
|
|
30
28
|
|
|
31
29
|
normalizeName(name){
|
|
32
30
|
return Inflector.instance.dasherize(name);
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
async importFile(params){
|
|
36
|
-
const { filePath, relativeFilePathWithoutExtension } = params;
|
|
37
|
-
if((await import(filePath)).default){
|
|
38
|
-
Client.instance.addModule(`
|
|
39
|
-
import { Component } from ${JSON.stringify(fileURLToPath(`${import.meta.url}/../index.js`))};
|
|
40
|
-
import include from ${JSON.stringify(filePath)};
|
|
41
|
-
Component.register(${JSON.stringify(relativeFilePathWithoutExtension)}, include);
|
|
42
|
-
`);
|
|
43
|
-
} else {
|
|
44
|
-
Client.instance.addModule(`
|
|
45
|
-
import ${JSON.stringify(filePath)};
|
|
46
|
-
`);
|
|
47
|
-
}
|
|
48
|
-
return importFile.call(this, params);
|
|
49
31
|
}
|
|
50
32
|
});
|
|
33
|
+
|
|
34
|
+
this.FileImporter.register('js', {
|
|
35
|
+
meta(){
|
|
36
|
+
const { importFile } = this.prototype;
|
|
37
|
+
|
|
38
|
+
this.include({
|
|
39
|
+
async importFile(params){
|
|
40
|
+
const { filePath, relativeFilePathWithoutExtension } = params;
|
|
41
|
+
if((await import(filePath)).default){
|
|
42
|
+
Client.instance.addModule(`
|
|
43
|
+
import { Component } from ${JSON.stringify(fileURLToPath(`${import.meta.url}/../index.js`))};
|
|
44
|
+
import include from ${JSON.stringify(filePath)};
|
|
45
|
+
Component.register(${JSON.stringify(relativeFilePathWithoutExtension)}, include);
|
|
46
|
+
`);
|
|
47
|
+
} else {
|
|
48
|
+
Client.instance.addModule(`
|
|
49
|
+
import ${JSON.stringify(filePath)};
|
|
50
|
+
`);
|
|
51
|
+
}
|
|
52
|
+
return importFile.call(this, params);
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
})
|
|
51
57
|
},
|
|
52
58
|
|
|
53
59
|
initialize(node, skipInit = false){
|
|
@@ -70,8 +76,9 @@ export const Component = Class.extend().include({
|
|
|
70
76
|
this.setTimeout(() => this.node.focus());
|
|
71
77
|
}
|
|
72
78
|
|
|
73
|
-
const { autosubmit, trigger } = this.data;
|
|
79
|
+
const { autosubmit, trigger, decorate } = this.data;
|
|
74
80
|
|
|
81
|
+
// replace with pinstripe-autosubmitter?
|
|
75
82
|
if(autosubmit){
|
|
76
83
|
let hash = JSON.stringify(this.values);
|
|
77
84
|
this.setInterval(() => {
|
|
@@ -83,6 +90,7 @@ export const Component = Class.extend().include({
|
|
|
83
90
|
}, 100);
|
|
84
91
|
}
|
|
85
92
|
|
|
93
|
+
// replace with script?
|
|
86
94
|
if(trigger){
|
|
87
95
|
this.setTimeout(() => {
|
|
88
96
|
this.trigger(trigger);
|
|
@@ -148,6 +156,10 @@ export const Component = Class.extend().include({
|
|
|
148
156
|
return out;
|
|
149
157
|
},
|
|
150
158
|
|
|
159
|
+
get parentsIncludingThis(){
|
|
160
|
+
return [this, ...this.parents];
|
|
161
|
+
},
|
|
162
|
+
|
|
151
163
|
get children(){
|
|
152
164
|
return [...this.node.childNodes].map(
|
|
153
165
|
node => this.constructor.instanceFor(node)
|
|
@@ -267,7 +279,7 @@ export const Component = Class.extend().include({
|
|
|
267
279
|
},
|
|
268
280
|
|
|
269
281
|
get document(){
|
|
270
|
-
return this.
|
|
282
|
+
return this.find('parentsIncludingThis', ({ isDocument }) => isDocument);
|
|
271
283
|
},
|
|
272
284
|
|
|
273
285
|
get overlay(){
|
|
@@ -277,6 +289,7 @@ export const Component = Class.extend().include({
|
|
|
277
289
|
get shadow(){
|
|
278
290
|
if(!this.node.shadowRoot){
|
|
279
291
|
this.node.attachShadow({ mode: 'open' });
|
|
292
|
+
this.shadow.observe({ add: true }, component => component.descendants);
|
|
280
293
|
this.shadow.patch(`<slot>`);
|
|
281
294
|
}
|
|
282
295
|
return Component.instanceFor(this.node.shadowRoot);
|
|
@@ -351,8 +364,8 @@ export const Component = Class.extend().include({
|
|
|
351
364
|
|
|
352
365
|
remove(){
|
|
353
366
|
if(this.realParent){
|
|
354
|
-
|
|
355
|
-
|
|
367
|
+
clean.call(this);
|
|
368
|
+
this.realParent.node.removeChild(this.node);
|
|
356
369
|
}
|
|
357
370
|
return this;
|
|
358
371
|
},
|
|
@@ -440,20 +453,25 @@ export const Component = Class.extend().include({
|
|
|
440
453
|
},
|
|
441
454
|
|
|
442
455
|
async fetch(url, options = {}){
|
|
456
|
+
const { minimumDelay = 0, ...otherOptions } = options;
|
|
443
457
|
const { progressBar } = this.document;
|
|
444
458
|
const frame = this.frame || this;
|
|
445
459
|
const normalizedUrl = new URL(url, frame.url);
|
|
446
460
|
const abortController = new AbortController();
|
|
447
461
|
this._registeredAbortControllers.push(abortController);
|
|
448
462
|
progressBar.start();
|
|
463
|
+
let minimumDelayTimeout;
|
|
449
464
|
const cleanUp = () => {
|
|
465
|
+
clearTimeout(minimumDelayTimeout);
|
|
450
466
|
this._registeredAbortControllers = this._registeredAbortControllers.filter(item => item !== abortController);
|
|
451
467
|
progressBar.stop();
|
|
452
468
|
};
|
|
453
469
|
try {
|
|
454
|
-
const
|
|
455
|
-
signal: abortController.signal
|
|
456
|
-
|
|
470
|
+
const promises = [
|
|
471
|
+
fetch(normalizedUrl, { signal: abortController.signal, ...otherOptions }),
|
|
472
|
+
new Promise(resolve => minimumDelayTimeout = setTimeout(resolve, minimumDelay))
|
|
473
|
+
];
|
|
474
|
+
const [ out ] = await Promise.all(promises);
|
|
457
475
|
cleanUp();
|
|
458
476
|
return out;
|
|
459
477
|
} catch(e){
|
|
@@ -480,7 +498,6 @@ export const Component = Class.extend().include({
|
|
|
480
498
|
const [ collection, selector ] = args;
|
|
481
499
|
return this[collection].filter(item => item.is(selector));
|
|
482
500
|
}
|
|
483
|
-
|
|
484
501
|
});
|
|
485
502
|
|
|
486
503
|
const matchesSelector = (() => {
|
|
@@ -497,6 +514,8 @@ function cleanChildren(){
|
|
|
497
514
|
}
|
|
498
515
|
|
|
499
516
|
function clean(){
|
|
517
|
+
this.trigger('clean', { bubbles: false });
|
|
518
|
+
|
|
500
519
|
[...this.node.childNodes].forEach(node => node._component && clean.call(node._component));
|
|
501
520
|
|
|
502
521
|
while(this._registeredEventListeners.length){
|
|
@@ -572,11 +591,12 @@ function patchAttributes(attributes){
|
|
|
572
591
|
const currentAttributes = this.attributes;
|
|
573
592
|
Object.keys(currentAttributes).forEach((key) => {
|
|
574
593
|
if(attributes[key] === undefined){
|
|
575
|
-
|
|
594
|
+
Element.prototype.removeAttribute.call(this.node, key); // work around for https://github.com/cypress-io/cypress/issues/26206
|
|
595
|
+
// this.node.removeAttribute(key);
|
|
576
596
|
}
|
|
577
597
|
})
|
|
578
598
|
Object.keys(attributes).forEach((key) => {
|
|
579
|
-
if(currentAttributes[key] != attributes[key]){
|
|
599
|
+
if(!currentAttributes.hasOwnProperty(key) || currentAttributes[key] != attributes[key]){
|
|
580
600
|
this.node.setAttribute(key, attributes[key]);
|
|
581
601
|
if(key == 'value'){
|
|
582
602
|
this.node.value = attributes[key];
|
package/lib/components/a.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { loadFrame,
|
|
2
|
+
import { loadFrame, getFrame } from "./helpers.js";
|
|
3
3
|
|
|
4
4
|
export default {
|
|
5
5
|
initialize(...args){
|
|
@@ -8,14 +8,20 @@ export default {
|
|
|
8
8
|
const { ignoreEventsFromChildren = false } = this.data;
|
|
9
9
|
this.on('click', (event) => {
|
|
10
10
|
if(ignoreEventsFromChildren && event.target != this) return;
|
|
11
|
-
const {
|
|
11
|
+
const { confirm, target = '_self', method = 'GET', href, placeholder } = { ...this.attributes, ...this.data };
|
|
12
12
|
if(new URL(href, window.location.href).host != window.location.host) return;
|
|
13
13
|
event.preventDefault();
|
|
14
14
|
event.stopPropagation();
|
|
15
|
-
|
|
16
|
-
if(action == 'remove') removeFrame.call(this, confirm, target);
|
|
15
|
+
loadFrame.call(this, confirm, target, method, href, placeholder);
|
|
17
16
|
});
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
const { target = '_self', method = 'GET', href, preload, placeholder } = { ...this.attributes, ...this.data };
|
|
19
|
+
if(method == 'GET' && target != '_blank'){
|
|
20
|
+
const frame = target == '_overlay' ? this.frame : getFrame.call(this, target);
|
|
21
|
+
if(preload != undefined) this.document.preload(new URL(href, frame.url));
|
|
22
|
+
if(placeholder != undefined) this.document.preload(new URL(placeholder, frame.url));
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
if(this.is('input, textarea')) this.on('keyup', (event) => this.trigger('click'));
|
|
20
26
|
}
|
|
21
|
-
};
|
|
27
|
+
};
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
|
|
2
|
+
import { loadCache } from "./helpers.js";
|
|
3
|
+
|
|
4
|
+
const preloading = {};
|
|
5
|
+
|
|
2
6
|
export default {
|
|
3
7
|
meta(){
|
|
4
8
|
this.include('pinstripe-frame');
|
|
@@ -25,8 +29,8 @@ export default {
|
|
|
25
29
|
return this.body.progressBar;
|
|
26
30
|
},
|
|
27
31
|
|
|
28
|
-
async load(url, options = {}){
|
|
29
|
-
const { replace, method = 'GET'
|
|
32
|
+
async load(url = this.url.toString(), options = {}){
|
|
33
|
+
const { replace, method = 'GET' } = options;
|
|
30
34
|
const previousUrl = this.url.toString();
|
|
31
35
|
const normalizedUrl = new URL(url, previousUrl).toString();
|
|
32
36
|
|
|
@@ -39,11 +43,16 @@ export default {
|
|
|
39
43
|
}
|
|
40
44
|
}
|
|
41
45
|
|
|
42
|
-
return this.constructor.for('pinstripe-frame').prototype.load.call(this, url,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
return this.constructor.for('pinstripe-frame').prototype.load.call(this, url, options);
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
async preload(url){
|
|
50
|
+
if(loadCache.get(url.toString())) return;
|
|
51
|
+
if(preloading[url.toString()]) return;
|
|
52
|
+
preloading[url.toString()] = true;
|
|
53
|
+
const response = await fetch(url);
|
|
54
|
+
const html = await response.text();
|
|
55
|
+
loadCache.put(url.toString(), html);
|
|
56
|
+
delete preloading[url.toString()];
|
|
48
57
|
}
|
|
49
58
|
};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
2
|
+
import { LruCache } from '../lru_cache.js';
|
|
3
|
+
|
|
4
|
+
export const loadCache = LruCache.new();
|
|
5
|
+
|
|
6
|
+
export function loadFrame(confirm, target, method, url, placeholderUrl){
|
|
3
7
|
if(confirm && !window.confirm(confirm)){
|
|
4
8
|
return;
|
|
5
9
|
}
|
|
@@ -21,31 +25,24 @@ export function loadFrame(confirm, target, method, url){
|
|
|
21
25
|
return;
|
|
22
26
|
}
|
|
23
27
|
|
|
28
|
+
if(placeholderUrl) placeholderUrl = new URL(placeholderUrl, this.frame.url);
|
|
29
|
+
|
|
24
30
|
if(method.match(/POST|PUT|PATCH/i)){
|
|
25
31
|
const formData = new FormData();
|
|
26
32
|
const values = this.values;
|
|
27
33
|
Object.keys(values).forEach((name) => formData.append(name, values[name]));
|
|
28
|
-
frame.load(url, { method, body: formData });
|
|
34
|
+
frame.load(url, { method, body: formData, placeholderUrl });
|
|
29
35
|
} else {
|
|
30
|
-
frame.load(url, { method });
|
|
36
|
+
frame.load(url, { method, placeholderUrl });
|
|
31
37
|
}
|
|
32
38
|
}
|
|
33
39
|
|
|
34
|
-
export function
|
|
35
|
-
if(confirm && !window.confirm(confirm)){
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const frame = getFrame.call(this, target);
|
|
40
|
-
if(frame) frame.remove();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function getFrame(target){
|
|
40
|
+
export function getFrame(target){
|
|
44
41
|
if(target == '_self') return this.frame;
|
|
45
42
|
if(target == '_top') return this.document;
|
|
46
43
|
if(target.match(/^_parent/)){
|
|
47
44
|
const index = target.split(/_/).length - 1;
|
|
48
45
|
return this.parents.filter(n => n.isFrame)[index];
|
|
49
46
|
}
|
|
50
|
-
return this.frame.
|
|
47
|
+
return this.frame.descendants.find(n => n.isFrame && n.data.name == target);
|
|
51
48
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
|
|
2
|
+
import { loadCache } from "./helpers.js";
|
|
3
|
+
|
|
2
4
|
export default {
|
|
3
5
|
initialize(...args){
|
|
4
6
|
this.constructor.parent.prototype.initialize.call(this, ...args);
|
|
@@ -30,18 +32,29 @@ export default {
|
|
|
30
32
|
);
|
|
31
33
|
},
|
|
32
34
|
|
|
35
|
+
loading: false,
|
|
36
|
+
|
|
33
37
|
async load(url = this.url, options = {}){
|
|
38
|
+
if(this.loading) return;
|
|
39
|
+
this.loading = true;
|
|
34
40
|
this.abort();
|
|
35
|
-
|
|
41
|
+
const { method = 'GET', placeholderUrl } = options;
|
|
42
|
+
const cachedHtml = method == 'GET' ? loadCache.get(url.toString()) : undefined;
|
|
43
|
+
const out = cachedHtml ? this.patch(cachedHtml) : undefined;
|
|
44
|
+
let minimumDelay = 0;
|
|
45
|
+
if(!cachedHtml && placeholderUrl){
|
|
46
|
+
const placeholderHtml = loadCache.get(placeholderUrl.toString());
|
|
47
|
+
if(placeholderHtml) {
|
|
48
|
+
this.patch(placeholderHtml);
|
|
49
|
+
minimumDelay = 300;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
36
52
|
this.url = url;
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}, otherOptions));
|
|
44
|
-
|
|
45
|
-
return this.patch(await response.text());
|
|
53
|
+
const response = await this.fetch(url, { minimumDelay, ...options });
|
|
54
|
+
const html = await response.text();
|
|
55
|
+
this.loading = false;
|
|
56
|
+
if(html == cachedHtml) return out;
|
|
57
|
+
if(method == 'GET') loadCache.put(url.toString(), html);
|
|
58
|
+
this.patch(html);
|
|
46
59
|
}
|
|
47
60
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import { Component } from '../component.js'
|
|
2
|
+
import { Component } from '../component.js';
|
|
3
|
+
import { Markdown } from '../markdown.js';
|
|
3
4
|
|
|
4
5
|
export default {
|
|
5
6
|
initialize(...args){
|
|
@@ -24,9 +25,7 @@ export default {
|
|
|
24
25
|
|
|
25
26
|
this.on('submit', () => {
|
|
26
27
|
const { value } = this.values;
|
|
27
|
-
|
|
28
|
-
formData.append('value', value);
|
|
29
|
-
previewFrame.load(previewFrame.url, { method: 'POST', body: formData });
|
|
28
|
+
previewFrame.patch(Markdown.render(value).toString());
|
|
30
29
|
anchorTextarea.value = value;
|
|
31
30
|
});
|
|
32
31
|
},
|