pome-ui 2.0.0-preview51 → 2.0.0-preview53
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 +1 -1
- package/pome-skeleton.css +15 -0
- package/pome-ui.dev.js +256 -25
- package/pome-ui.dev.min.js +1 -1
- package/pome-ui.js +256 -25
- package/pome-ui.min.js +4 -4
package/package.json
CHANGED
package/pome-skeleton.css
CHANGED
|
@@ -119,3 +119,18 @@
|
|
|
119
119
|
background-size: 400% 100%;
|
|
120
120
|
animation: _pome-sk-shimmer 1.4s ease infinite;
|
|
121
121
|
}
|
|
122
|
+
|
|
123
|
+
/* Layout skeleton overlay – covers viewport while Layout created() resolves.
|
|
124
|
+
* The real layout content (including <render-body> container) stays in the DOM
|
|
125
|
+
* behind this overlay so that container.open() can mount the page immediately.
|
|
126
|
+
*/
|
|
127
|
+
._pome-layout-skeleton {
|
|
128
|
+
position: fixed;
|
|
129
|
+
top: 0;
|
|
130
|
+
left: 0;
|
|
131
|
+
right: 0;
|
|
132
|
+
bottom: 0;
|
|
133
|
+
z-index: 9999;
|
|
134
|
+
background: #fff;
|
|
135
|
+
overflow: hidden;
|
|
136
|
+
}
|
package/pome-ui.dev.js
CHANGED
|
@@ -18201,6 +18201,37 @@ function build(options, exports) {
|
|
|
18201
18201
|
// This function is kept as a hook point for future use.
|
|
18202
18202
|
}
|
|
18203
18203
|
|
|
18204
|
+
// Registry: kebab-name → pre-rendered skeleton HTML string
|
|
18205
|
+
var _componentSkeletonRegistry = {};
|
|
18206
|
+
|
|
18207
|
+
function _toKebabCase(name) {
|
|
18208
|
+
// ScalableSidebar → scalable-sidebar, menuBar → menu-bar
|
|
18209
|
+
return name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
18210
|
+
}
|
|
18211
|
+
|
|
18212
|
+
function _registerComponentSkeleton(name, opt, rawHtml, skeletonFileHtml) {
|
|
18213
|
+
// skeletonFileHtml: content of <component>.skeleton.html (takes top priority)
|
|
18214
|
+
// opt.skeleton === string: inline HTML declared in the JS
|
|
18215
|
+
// opt.skeleton === true: auto-generate from rawHtml
|
|
18216
|
+
var hasFile = !!skeletonFileHtml;
|
|
18217
|
+
var hasInline = opt && typeof opt.skeleton === 'string';
|
|
18218
|
+
var hasAuto = opt && opt.skeleton === true;
|
|
18219
|
+
if (!hasFile && !hasInline && !hasAuto) return;
|
|
18220
|
+
var kebab = _toKebabCase(name);
|
|
18221
|
+
var skHtml = hasFile ? skeletonFileHtml
|
|
18222
|
+
: hasInline ? opt.skeleton
|
|
18223
|
+
: _generateSkeletonHtml(rawHtml || (opt && opt.template) || '');
|
|
18224
|
+
_componentSkeletonRegistry[kebab] = skHtml;
|
|
18225
|
+
}
|
|
18226
|
+
|
|
18227
|
+
// --- Extract a plain numeric value from a bound attribute like :default-width="230" ---
|
|
18228
|
+
function _extractBoundNumber(el, attr) {
|
|
18229
|
+
var val = el.getAttribute(':' + attr);
|
|
18230
|
+
if (!val) return null;
|
|
18231
|
+
var m = val.match(/^(\d+(?:\.\d+)?)/);
|
|
18232
|
+
return m ? m[1] : null;
|
|
18233
|
+
}
|
|
18234
|
+
|
|
18204
18235
|
// --- Known HTML tags for skeleton (custom components will be unwrapped) ---
|
|
18205
18236
|
var _knownHtmlTags = null;
|
|
18206
18237
|
function _isKnownHtmlTag(tag) {
|
|
@@ -18304,9 +18335,57 @@ function build(options, exports) {
|
|
|
18304
18335
|
|
|
18305
18336
|
// 4. Replace custom/unknown elements with <div>, keep children
|
|
18306
18337
|
if (!_isKnownHtmlTag(tag)) {
|
|
18338
|
+
// Check if this component has declared its own skeleton
|
|
18339
|
+
var registeredSkeleton = _componentSkeletonRegistry[tag];
|
|
18340
|
+
if (registeredSkeleton) {
|
|
18341
|
+
// Parse the registered skeleton HTML and replace the element with it
|
|
18342
|
+
var skWrap = doc.createElement('div');
|
|
18343
|
+
skWrap.innerHTML = registeredSkeleton;
|
|
18344
|
+
if (child.getAttribute('class')) skWrap.setAttribute('class', child.getAttribute('class'));
|
|
18345
|
+
var existingStyleReg = child.getAttribute('style') || '';
|
|
18346
|
+
var styleExtraReg = '';
|
|
18347
|
+
var defWReg = _extractBoundNumber(child, 'default-width') || child.getAttribute('default-width');
|
|
18348
|
+
var minWReg = _extractBoundNumber(child, 'min-width') || child.getAttribute('min-width');
|
|
18349
|
+
if (defWReg) styleExtraReg += ';width:' + defWReg + 'px;flex-shrink:0';
|
|
18350
|
+
if (minWReg) styleExtraReg += ';min-width:' + minWReg + 'px';
|
|
18351
|
+
var combinedStyleReg = (existingStyleReg + styleExtraReg).replace(/^;+/, '');
|
|
18352
|
+
if (combinedStyleReg) skWrap.setAttribute('style', combinedStyleReg);
|
|
18353
|
+
// Replace the custom element with a wrapper containing the skeleton.
|
|
18354
|
+
// Use a fragment-like approach: move skWrap's children directly
|
|
18355
|
+
// so we don't add an extra div when the registered HTML already has one.
|
|
18356
|
+
var skNodes = Array.from(skWrap.childNodes);
|
|
18357
|
+
if (skNodes.length === 1 && skNodes[0].nodeType === 1) {
|
|
18358
|
+
var skRoot = skNodes[0];
|
|
18359
|
+
// Merge class / style from host element onto the single root node
|
|
18360
|
+
if (child.getAttribute('class')) skRoot.classList && skRoot.setAttribute('class', (skRoot.getAttribute('class') || '') + ' ' + child.getAttribute('class'));
|
|
18361
|
+
if (combinedStyleReg) skRoot.setAttribute('style', (skRoot.getAttribute('style') ? skRoot.getAttribute('style') + ';' : '') + combinedStyleReg);
|
|
18362
|
+
node.replaceChild(skRoot, child);
|
|
18363
|
+
i++;
|
|
18364
|
+
} else {
|
|
18365
|
+
while (skWrap.firstChild) node.insertBefore(skWrap.firstChild, child);
|
|
18366
|
+
node.removeChild(child);
|
|
18367
|
+
// i stays the same since we removed child and inserted nodes before it
|
|
18368
|
+
}
|
|
18369
|
+
continue;
|
|
18370
|
+
}
|
|
18371
|
+
|
|
18307
18372
|
var div = doc.createElement('div');
|
|
18308
18373
|
if (child.getAttribute('class')) div.setAttribute('class', child.getAttribute('class'));
|
|
18309
|
-
|
|
18374
|
+
|
|
18375
|
+
// Reconstruct inline style, preserving explicit style + any bound sizing attrs
|
|
18376
|
+
var existingStyle = child.getAttribute('style') || '';
|
|
18377
|
+
var styleExtra = '';
|
|
18378
|
+
// :default-width / default-width → width + flex-shrink:0 so sidebar-like
|
|
18379
|
+
// components keep their width in the skeleton
|
|
18380
|
+
var defW = _extractBoundNumber(child, 'default-width') || child.getAttribute('default-width');
|
|
18381
|
+
var minW = _extractBoundNumber(child, 'min-width') || child.getAttribute('min-width');
|
|
18382
|
+
var maxW = _extractBoundNumber(child, 'max-width') || child.getAttribute('max-width');
|
|
18383
|
+
if (defW) styleExtra += ';width:' + defW + 'px;flex-shrink:0';
|
|
18384
|
+
if (minW) styleExtra += ';min-width:' + minW + 'px';
|
|
18385
|
+
if (maxW) styleExtra += ';max-width:' + maxW + 'px';
|
|
18386
|
+
var combinedStyle = (existingStyle + styleExtra).replace(/^;+/, '');
|
|
18387
|
+
if (combinedStyle) div.setAttribute('style', combinedStyle);
|
|
18388
|
+
|
|
18310
18389
|
while (child.firstChild) div.appendChild(child.firstChild);
|
|
18311
18390
|
node.replaceChild(div, child);
|
|
18312
18391
|
_skeletonStructurePass(div, doc); // recurse into replacement
|
|
@@ -18427,6 +18506,24 @@ function build(options, exports) {
|
|
|
18427
18506
|
});
|
|
18428
18507
|
};
|
|
18429
18508
|
|
|
18509
|
+
// Fetch a .skeleton.html file, returning null when the file does not
|
|
18510
|
+
// physically exist. Unlike _httpGet, this guards against SPA catch-all
|
|
18511
|
+
// routes that return the app entry page (status 200) for any URL:
|
|
18512
|
+
// if the response body starts with "<!DOCTYPE" or "<html" it is treated
|
|
18513
|
+
// as a missing file and null is returned.
|
|
18514
|
+
function _httpGetSkeletonFile(url) {
|
|
18515
|
+
return _httpGet(url).then(function (text) {
|
|
18516
|
+
if (!text) return null;
|
|
18517
|
+
var trimmed = text.replace(/^[\s\uFEFF]+/, ''); // strip BOM / whitespace
|
|
18518
|
+
if (/^<!DOCTYPE/i.test(trimmed) || /^<html/i.test(trimmed)) {
|
|
18519
|
+
return null; // SPA fallback page, not a real skeleton file
|
|
18520
|
+
}
|
|
18521
|
+
return text;
|
|
18522
|
+
}).catch(function () {
|
|
18523
|
+
return null;
|
|
18524
|
+
});
|
|
18525
|
+
};
|
|
18526
|
+
|
|
18430
18527
|
function _serializeOptionsToUrl(options) {
|
|
18431
18528
|
var fields = Object.getOwnPropertyNames(options);
|
|
18432
18529
|
var str = '';
|
|
@@ -18647,19 +18744,27 @@ function build(options, exports) {
|
|
|
18647
18744
|
});
|
|
18648
18745
|
})
|
|
18649
18746
|
.then(function (component) {
|
|
18650
|
-
var
|
|
18747
|
+
var htmlPromise;
|
|
18651
18748
|
if (mobile) {
|
|
18652
|
-
|
|
18749
|
+
htmlPromise = _httpExist(url + '.m.html').then(function (res) {
|
|
18653
18750
|
return _httpGet(url + (res ? '.m.html' : '.html'));
|
|
18654
18751
|
});
|
|
18655
18752
|
} else {
|
|
18656
|
-
|
|
18753
|
+
htmlPromise = _httpGet(url + '.html');
|
|
18657
18754
|
}
|
|
18755
|
+
// Load .skeleton.html in parallel; null if not found
|
|
18756
|
+
var skeletonFilePromise = _httpGetSkeletonFile(url + '.skeleton.html');
|
|
18658
18757
|
|
|
18659
|
-
return
|
|
18758
|
+
return Promise.all([htmlPromise, skeletonFilePromise]).then(function (results) {
|
|
18759
|
+
var template = results[0];
|
|
18760
|
+
var skeletonFileHtml = results[1];
|
|
18660
18761
|
if (!component.template) {
|
|
18661
18762
|
component.template = _sanitizeTemplate(template);
|
|
18662
18763
|
}
|
|
18764
|
+
// .skeleton.html file takes top priority over skeleton: true / inline string
|
|
18765
|
+
if (skeletonFileHtml && component.skeleton) {
|
|
18766
|
+
component.skeleton = skeletonFileHtml;
|
|
18767
|
+
}
|
|
18663
18768
|
if (component.delayOpen) {
|
|
18664
18769
|
var dom = new DOMParser().parseFromString(component.template, 'text/html');
|
|
18665
18770
|
var children = dom.querySelector('body').children;
|
|
@@ -18684,7 +18789,8 @@ function build(options, exports) {
|
|
|
18684
18789
|
component.template = ret;
|
|
18685
18790
|
}
|
|
18686
18791
|
_patchComponent(url, component);
|
|
18687
|
-
_wrapTemplateWithSkeleton
|
|
18792
|
+
// _wrapTemplateWithSkeleton is deferred to after _loadComponents
|
|
18793
|
+
// so the component skeleton registry is fully populated.
|
|
18688
18794
|
return Promise.resolve(component);
|
|
18689
18795
|
});
|
|
18690
18796
|
})
|
|
@@ -18718,6 +18824,8 @@ function build(options, exports) {
|
|
|
18718
18824
|
|
|
18719
18825
|
return Promise.all([pageCssPromise, _loadComponents(components, url)]).then(function (results) {
|
|
18720
18826
|
var components = results[1];
|
|
18827
|
+
// Wrap skeleton template now that component registry is fully populated.
|
|
18828
|
+
_wrapTemplateWithSkeleton(component);
|
|
18721
18829
|
var ret = Vue.createApp(component);
|
|
18722
18830
|
|
|
18723
18831
|
for (var i = 0; i < components.length; ++i) {
|
|
@@ -18947,6 +19055,48 @@ function build(options, exports) {
|
|
|
18947
19055
|
|
|
18948
19056
|
return _loadComponents(options.components || [], layout)
|
|
18949
19057
|
.then(function (components) {
|
|
19058
|
+
// Inject layout skeleton overlay now that component registry is populated.
|
|
19059
|
+
if (options._pomeLayoutSkeleton) {
|
|
19060
|
+
var ls = options._pomeLayoutSkeleton;
|
|
19061
|
+
var skHtml = ls.skeletonOverride
|
|
19062
|
+
? ls.skeletonOverride
|
|
19063
|
+
: (function () {
|
|
19064
|
+
var html = _generateSkeletonHtml(ls.skeletonBody);
|
|
19065
|
+
// Post-process: remove empty top-level nodes (modals etc.),
|
|
19066
|
+
// set flex:1 on the main content area next to the sidebar.
|
|
19067
|
+
var tmp = document.createElement('div');
|
|
19068
|
+
tmp.innerHTML = html;
|
|
19069
|
+
var topChildren = Array.from(tmp.children);
|
|
19070
|
+
var sidebarIdx = -1;
|
|
19071
|
+
for (var ci = 0; ci < topChildren.length; ci++) {
|
|
19072
|
+
var ch = topChildren[ci];
|
|
19073
|
+
if (!ch.innerHTML.trim()) {
|
|
19074
|
+
tmp.removeChild(ch);
|
|
19075
|
+
topChildren = Array.from(tmp.children);
|
|
19076
|
+
ci--;
|
|
19077
|
+
continue;
|
|
19078
|
+
}
|
|
19079
|
+
if (sidebarIdx < 0 && /\bwidth\s*:/.test(ch.getAttribute('style') || '')) {
|
|
19080
|
+
sidebarIdx = ci;
|
|
19081
|
+
} else if (sidebarIdx >= 0 && ci === sidebarIdx + 1) {
|
|
19082
|
+
ch.style.flex = '1';
|
|
19083
|
+
ch.style.minWidth = '0';
|
|
19084
|
+
ch.style.overflow = 'hidden';
|
|
19085
|
+
}
|
|
19086
|
+
}
|
|
19087
|
+
return tmp.innerHTML;
|
|
19088
|
+
})();
|
|
19089
|
+
var appEl = document.querySelector(ls.appId);
|
|
19090
|
+
if (appEl) {
|
|
19091
|
+
var skDiv = document.createElement('div');
|
|
19092
|
+
skDiv.setAttribute('v-if', 'pomeSkeletonLoading');
|
|
19093
|
+
skDiv.className = '_pome-skeleton _pome-layout-skeleton ' + ls.bodyClass;
|
|
19094
|
+
if (ls.overlayStyle) skDiv.setAttribute('style', ls.overlayStyle);
|
|
19095
|
+
skDiv.innerHTML = skHtml;
|
|
19096
|
+
appEl.insertBefore(skDiv, appEl.firstChild);
|
|
19097
|
+
}
|
|
19098
|
+
delete options._pomeLayoutSkeleton;
|
|
19099
|
+
}
|
|
18950
19100
|
var app = Vue.createApp(options || {});
|
|
18951
19101
|
for (var i = 0; i < components.length; ++i) {
|
|
18952
19102
|
var com = components[i];
|
|
@@ -19448,27 +19598,59 @@ function build(options, exports) {
|
|
|
19448
19598
|
this.createdPromise = ret;
|
|
19449
19599
|
if (options.skeleton) {
|
|
19450
19600
|
var self = this;
|
|
19451
|
-
|
|
19452
|
-
|
|
19453
|
-
if
|
|
19454
|
-
|
|
19455
|
-
|
|
19601
|
+
var skeletonShowing = false;
|
|
19602
|
+
var resolved = false;
|
|
19603
|
+
// Only activate the skeleton if the promise is still pending
|
|
19604
|
+
// after skeletonDelay ms. If it resolves faster, skip the
|
|
19605
|
+
// skeleton entirely to avoid a flash.
|
|
19606
|
+
var skeletonDelay = typeof options.skeletonDelay === 'number'
|
|
19607
|
+
? options.skeletonDelay : 1000;
|
|
19608
|
+
var skeletonTimer = setTimeout(function () {
|
|
19609
|
+
if (resolved) return;
|
|
19610
|
+
skeletonShowing = true;
|
|
19611
|
+
self.pomeSkeletonLoading = true;
|
|
19612
|
+
// Skeleton is now visible. If delayOpen is also set, kick
|
|
19613
|
+
// off its animation so it's primed when skeleton disappears.
|
|
19614
|
+
if (options.delayOpen) {
|
|
19615
|
+
self.$nextTick(function () {
|
|
19456
19616
|
requestAnimationFrame(function () {
|
|
19457
|
-
|
|
19617
|
+
requestAnimationFrame(function () {
|
|
19618
|
+
self.pomeDelayOpening = false;
|
|
19619
|
+
});
|
|
19458
19620
|
});
|
|
19621
|
+
setTimeout(function () {
|
|
19622
|
+
self.pomeDelayOpened = false;
|
|
19623
|
+
}, options.delayOpen);
|
|
19459
19624
|
});
|
|
19460
|
-
|
|
19461
|
-
|
|
19462
|
-
}, options.delayOpen);
|
|
19463
|
-
});
|
|
19464
|
-
}
|
|
19625
|
+
}
|
|
19626
|
+
}, skeletonDelay);
|
|
19465
19627
|
ret.then(function () {
|
|
19628
|
+
resolved = true;
|
|
19629
|
+
clearTimeout(skeletonTimer);
|
|
19466
19630
|
self.pomeSkeletonLoading = false;
|
|
19631
|
+
// Fast path: resolved before skeleton appeared – still need
|
|
19632
|
+
// to run the delayOpen animation now.
|
|
19633
|
+
if (!skeletonShowing && options.delayOpen) {
|
|
19634
|
+
self.$nextTick(function () {
|
|
19635
|
+
requestAnimationFrame(function () {
|
|
19636
|
+
requestAnimationFrame(function () {
|
|
19637
|
+
self.pomeDelayOpening = false;
|
|
19638
|
+
});
|
|
19639
|
+
});
|
|
19640
|
+
setTimeout(function () {
|
|
19641
|
+
self.pomeDelayOpened = false;
|
|
19642
|
+
}, options.delayOpen);
|
|
19643
|
+
});
|
|
19644
|
+
}
|
|
19467
19645
|
}).catch(function (err) {
|
|
19646
|
+
resolved = true;
|
|
19647
|
+
clearTimeout(skeletonTimer);
|
|
19648
|
+
self.pomeSkeletonLoading = false;
|
|
19468
19649
|
console.error('[SKELETON] created Promise REJECTED:', err);
|
|
19469
19650
|
});
|
|
19470
19651
|
}
|
|
19471
19652
|
} else if (options.skeleton) {
|
|
19653
|
+
// Sync created – nothing to wait for, ensure skeleton is off.
|
|
19472
19654
|
this.pomeSkeletonLoading = false;
|
|
19473
19655
|
}
|
|
19474
19656
|
|
|
@@ -19608,9 +19790,17 @@ function build(options, exports) {
|
|
|
19608
19790
|
if (layout) {
|
|
19609
19791
|
var layoutName = layout + (mobile ? '.m' : '');
|
|
19610
19792
|
var _layoutHtml;
|
|
19611
|
-
|
|
19612
|
-
|
|
19613
|
-
|
|
19793
|
+
var _layoutSkeletonFileHtml; // hoisted so LayoutNext closure can access it
|
|
19794
|
+
// Load layout .html, .js and optional .skeleton.html all in parallel
|
|
19795
|
+
return Promise.all([
|
|
19796
|
+
_httpGet(layoutName + '.html'),
|
|
19797
|
+
_httpGet(layout + '.js'),
|
|
19798
|
+
_httpGetSkeletonFile(layoutName + '.skeleton.html')
|
|
19799
|
+
]).then(function (layoutResults) {
|
|
19800
|
+
_layoutHtml = layoutResults[0];
|
|
19801
|
+
var js = layoutResults[1];
|
|
19802
|
+
_layoutSkeletonFileHtml = layoutResults[2]; // null when absent
|
|
19803
|
+
return js;
|
|
19614
19804
|
}).then(function (js) {
|
|
19615
19805
|
var _opt = null;
|
|
19616
19806
|
var Layout = function (options) {
|
|
@@ -19634,6 +19824,37 @@ function build(options, exports) {
|
|
|
19634
19824
|
var appId = 'pomelo-' + ticks;
|
|
19635
19825
|
var containerId = 'container-' + ticks;
|
|
19636
19826
|
var body = document.querySelector('body').innerHTML.replace('<render-body></render-body>', '<div id="' + containerId + '"></div>');
|
|
19827
|
+
// Layout skeleton: defer generation to Root→_loadComponents so the
|
|
19828
|
+
// component skeleton registry is fully populated before we generate HTML.
|
|
19829
|
+
if (options.skeleton) {
|
|
19830
|
+
_ensureSkeletonStyle();
|
|
19831
|
+
var bodyEl = document.querySelector('body');
|
|
19832
|
+
var bodyClass = bodyEl.getAttribute('class') || '';
|
|
19833
|
+
var bodyInlineStyle = bodyEl.getAttribute('style') || '';
|
|
19834
|
+
var bodyComputed = window.getComputedStyle(bodyEl);
|
|
19835
|
+
var overlayStyle = bodyInlineStyle;
|
|
19836
|
+
if (bodyComputed.display && bodyComputed.display.indexOf('flex') >= 0) {
|
|
19837
|
+
if (overlayStyle) overlayStyle += ';';
|
|
19838
|
+
overlayStyle += 'display:' + bodyComputed.display
|
|
19839
|
+
+ ';flex-direction:' + bodyComputed.flexDirection
|
|
19840
|
+
+ ';align-items:' + bodyComputed.alignItems;
|
|
19841
|
+
} else {
|
|
19842
|
+
if (overlayStyle) overlayStyle += ';';
|
|
19843
|
+
overlayStyle += 'display:flex;flex-direction:row;align-items:stretch';
|
|
19844
|
+
}
|
|
19845
|
+
var skeletonBody = body.replace('<div id="' + containerId + '"></div>', '<div style="flex:1;min-width:0"></div>');
|
|
19846
|
+
// Store pending data; Root will inject the overlay div after
|
|
19847
|
+
// _loadComponents so registered component skeletons are available.
|
|
19848
|
+
options._pomeLayoutSkeleton = {
|
|
19849
|
+
appId: '#' + appId,
|
|
19850
|
+
bodyClass: bodyClass,
|
|
19851
|
+
overlayStyle: overlayStyle,
|
|
19852
|
+
skeletonBody: skeletonBody,
|
|
19853
|
+
// Priority: .skeleton.html file > inline string in JS
|
|
19854
|
+
skeletonOverride: _layoutSkeletonFileHtml
|
|
19855
|
+
|| (typeof options.skeleton === 'string' ? options.skeleton : null)
|
|
19856
|
+
};
|
|
19857
|
+
}
|
|
19637
19858
|
_ensureVueCloakStyle();
|
|
19638
19859
|
document.querySelector('body').innerHTML = '<div id="' + appId + '" class="_pome-vue-cloak">' + body + '</div>';
|
|
19639
19860
|
options.template = null;
|
|
@@ -19807,10 +20028,15 @@ function build(options, exports) {
|
|
|
19807
20028
|
var _html;
|
|
19808
20029
|
var _name;
|
|
19809
20030
|
var _opt;
|
|
19810
|
-
|
|
19811
|
-
|
|
19812
|
-
|
|
19813
|
-
|
|
20031
|
+
// Load .html, .js and optional .skeleton.html in parallel
|
|
20032
|
+
return Promise.all([
|
|
20033
|
+
_httpGet(c + '.html'),
|
|
20034
|
+
_httpGet(c + '.js'),
|
|
20035
|
+
_httpGetSkeletonFile(c + '.skeleton.html')
|
|
20036
|
+
]).then(function (results) {
|
|
20037
|
+
_html = results[0];
|
|
20038
|
+
var comJs = results[1];
|
|
20039
|
+
var skeletonFileHtml = results[2]; // null when file does not exist
|
|
19814
20040
|
var Component = function (name, options) {
|
|
19815
20041
|
_opt = options;
|
|
19816
20042
|
_name = name;
|
|
@@ -19824,6 +20050,9 @@ function build(options, exports) {
|
|
|
19824
20050
|
}
|
|
19825
20051
|
_hookSetup(_opt);
|
|
19826
20052
|
_patchComponent(c, _opt);
|
|
20053
|
+
// Register the component's skeleton BEFORE wrapping the template,
|
|
20054
|
+
// so _generateSkeletonHtml still sees the clean original template.
|
|
20055
|
+
_registerComponentSkeleton(_name, _opt, _opt.template, skeletonFileHtml);
|
|
19827
20056
|
_wrapTemplateWithSkeleton(_opt);
|
|
19828
20057
|
_hookData(_opt);
|
|
19829
20058
|
var cssPreloadPromise = Promise.resolve();
|
|
@@ -19933,7 +20162,9 @@ function build(options, exports) {
|
|
|
19933
20162
|
var data = func1.call(this, app);
|
|
19934
20163
|
var data2 = { pomeUiSubTitles: [] };
|
|
19935
20164
|
if (opt.skeleton) {
|
|
19936
|
-
|
|
20165
|
+
// Start hidden; skeleton is only shown after skeletonDelay ms
|
|
20166
|
+
// to avoid a flash when loading completes quickly.
|
|
20167
|
+
data2.pomeSkeletonLoading = false;
|
|
19937
20168
|
}
|
|
19938
20169
|
if (opt.skeleton && opt.delayOpen) {
|
|
19939
20170
|
data2.pomeDelayOpened = true;
|