estreui 1.2.4 → 1.2.6
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/README.md +10 -6
- package/package.json +10 -2
- package/scripts/estreUi-main.js +28 -4
- package/scripts/estreUi-pageManager.js +42 -0
- package/scripts/estreUi-pageModel.js +26 -3
- package/serviceWorker.js +2 -2
package/README.md
CHANGED
|
@@ -125,9 +125,11 @@ EstreUI pages have a distinct lifecycle, similar to Android Activities:
|
|
|
125
125
|
1. **onBring**: Page is being prepared. Since the Active Struct hasn't been called yet, this is a good place for element creation tasks involving handles.
|
|
126
126
|
2. **onOpen**: Page is opening (transition start). Called only once.
|
|
127
127
|
3. **onShow**: Page is fully visible.
|
|
128
|
-
4. **
|
|
129
|
-
5. **
|
|
130
|
-
6. **
|
|
128
|
+
4. **onFocus**: Page takes active focus (after `onShow`, and on window/tab return). Second arg `isFirstFocus` is true only on the first focus of the instance. Return `true` to opt out of the default in-page autoFocus.
|
|
129
|
+
5. **onBlur**: Page loses active focus (before `onHide`, and on window/tab leave). `handle.isClosing` indicates the final blur along a close path.
|
|
130
|
+
6. **onHide**: Page is hidden (covered by another page or closed).
|
|
131
|
+
7. **onClose**: Page is closed. Called only once.
|
|
132
|
+
8. **onRelease**: Page resources are released.
|
|
131
133
|
|
|
132
134
|
* **onBack**: Called when back navigation is triggered. Return `true` to cancel the default action.
|
|
133
135
|
* **onReload**: Called when page reload is triggered. Return `true` to cancel the default action (which is closing and reopening the page).
|
|
@@ -394,9 +396,11 @@ EstreUI 페이지는 Android Activity와 유사한 뚜렷한 라이프사이클
|
|
|
394
396
|
1. **onBring**: 페이지가 준비되는 중입니다. Active struct가 호출되기 이전이므로 각종 handle 등을 포함하는 element 생성 작업을 할 수 있습니다.
|
|
395
397
|
2. **onOpen**: 페이지가 열리는 중입니다 (전환 시작). 1회만 호출됩니다.
|
|
396
398
|
3. **onShow**: 페이지가 완전히 보입니다.
|
|
397
|
-
4. **
|
|
398
|
-
5. **
|
|
399
|
-
6. **
|
|
399
|
+
4. **onFocus**: 페이지가 활성 포커스를 받습니다 (`onShow` 이후, 그리고 창/탭 복귀 시). 두 번째 인자 `isFirstFocus` 는 이 인스턴스의 최초 포커스일 때만 `true`. `true` 를 반환하면 기본 autoFocus(페이지 내 DOM 포커스 이동)를 스킵합니다.
|
|
400
|
+
5. **onBlur**: 페이지가 포커스를 잃습니다 (`onHide` 직전, 그리고 창/탭 이탈 시). 닫힘 경로상 최종 blur 여부는 `handle.isClosing` 으로 식별합니다.
|
|
401
|
+
6. **onHide**: 페이지가 숨겨졌습니다 (다른 페이지에 가려지거나 닫힘).
|
|
402
|
+
7. **onClose**: 페이지가 닫혔습니다. 1회만 호출됩니다.
|
|
403
|
+
8. **onRelease**: 페이지 리소스가 해제됩니다.
|
|
400
404
|
|
|
401
405
|
* **onBack**: Back navigation이 호출될 때 실행되며, true를 반환할 경우 기본 작동이 취소됩니다.
|
|
402
406
|
* **onReload**: 페이지를 새로고침하려고 할 때 호출됩니다. true를 반환할 경우 기본 작동이 취소됩니다. 기본 작동은 해당 페이지를 닫은 후 다시 열어주는 것입니다.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "estreui",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "EstreUI Core Library - A comprehensive UI framework for web applications",
|
|
5
5
|
"main": "scripts/estreUi.js",
|
|
6
6
|
"files": [
|
|
@@ -40,6 +40,14 @@
|
|
|
40
40
|
],
|
|
41
41
|
"author": "SoliEstre",
|
|
42
42
|
"license": "MIT",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/SoliEstre/EstreUI.js.git"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://estreui.mpsolutions.kr",
|
|
48
|
+
"bugs": {
|
|
49
|
+
"url": "https://github.com/SoliEstre/EstreUI.js/issues"
|
|
50
|
+
},
|
|
43
51
|
"dependencies": {
|
|
44
52
|
"jquery": "^4.0.0",
|
|
45
53
|
"jcodd": "^0.9.0",
|
|
@@ -47,4 +55,4 @@
|
|
|
47
55
|
"modernism": "^0.7.2",
|
|
48
56
|
"alienese": "^0.7.2"
|
|
49
57
|
}
|
|
50
|
-
}
|
|
58
|
+
}
|
package/scripts/estreUi-main.js
CHANGED
|
@@ -387,6 +387,27 @@ const estreUi = {
|
|
|
387
387
|
this.onBlur();
|
|
388
388
|
});
|
|
389
389
|
|
|
390
|
+
// C (roadmap #006) — visibilitychange routes to onFocus/onBlur.
|
|
391
|
+
// More reliable on mobile browsers than window focus/blur, especially
|
|
392
|
+
// on Android WebView where native focus changes may not surface as JS events.
|
|
393
|
+
// Idempotent via the pageHandle.isFocused guard, so duplication with
|
|
394
|
+
// window focus/blur is harmless.
|
|
395
|
+
document.addEventListener("visibilitychange", () => {
|
|
396
|
+
if (window.isDebug) console.log(`[visibilitychange] state=${document.visibilityState} hasFocus=${document.hasFocus()}`);
|
|
397
|
+
if (document.visibilityState === "visible") this.onFocus();
|
|
398
|
+
else this.onBlur();
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// A (roadmap #006) — track lastFocusedElement on the topmost showing handle.
|
|
402
|
+
// focusin bubbles (unlike blur), so a single document-level capture covers
|
|
403
|
+
// every page. Used by phase B's autoFocus to restore the prior focus point.
|
|
404
|
+
document.addEventListener("focusin", (e) => {
|
|
405
|
+
const topHandle = this.showingTopArticle ?? this.mainCurrentOnTop;
|
|
406
|
+
if (topHandle != null && topHandle.host?.contains(e.target)) {
|
|
407
|
+
topHandle.lastFocusedElement = e.target;
|
|
408
|
+
}
|
|
409
|
+
}, true);
|
|
410
|
+
|
|
390
411
|
if (setOnReady) this.checkOnReady();
|
|
391
412
|
});
|
|
392
413
|
},
|
|
@@ -1575,12 +1596,15 @@ const estreUi = {
|
|
|
1575
1596
|
|
|
1576
1597
|
|
|
1577
1598
|
async onFocus() {
|
|
1578
|
-
|
|
1579
|
-
|
|
1599
|
+
const top = this.showingTopArticle ?? this.mainCurrentOnTop;
|
|
1600
|
+
if (window.isDebug) console.log(`[estreUi.onFocus] visibility=${document.visibilityState} hasFocus=${document.hasFocus()} top=${top?.pid ?? "(none)"}`);
|
|
1601
|
+
top?.focus();
|
|
1580
1602
|
},
|
|
1581
|
-
|
|
1603
|
+
|
|
1582
1604
|
async onBlur() {
|
|
1583
|
-
|
|
1605
|
+
const top = this.showingTopArticle ?? this.mainCurrentOnTop;
|
|
1606
|
+
if (window.isDebug) console.log(`[estreUi.onBlur] visibility=${document.visibilityState} hasFocus=${document.hasFocus()} top=${top?.pid ?? "(none)"}`);
|
|
1607
|
+
await top?.blur();
|
|
1584
1608
|
},
|
|
1585
1609
|
|
|
1586
1610
|
|
|
@@ -521,6 +521,48 @@ class EstreUiPageManager {
|
|
|
521
521
|
});
|
|
522
522
|
});
|
|
523
523
|
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Default auto-focus policy for a newly-focused page handle.
|
|
527
|
+
* Priority:
|
|
528
|
+
* 1. On repeat focus, restore `handle.lastFocusedElement` if still in the DOM.
|
|
529
|
+
* 2. First `[data-autofocus]` element inside the host.
|
|
530
|
+
* 3. First tab-reachable focusable element inside the host.
|
|
531
|
+
* 4. Otherwise no-op.
|
|
532
|
+
* Invoked from `pageHandle.onFocus()` when `handler.onFocus` does not return true.
|
|
533
|
+
* Projects can override on an `EstreUiCustomPageManager` subclass if a different policy is needed.
|
|
534
|
+
* @param {EstrePageHandle} handle - The page handle receiving focus.
|
|
535
|
+
* @param {boolean} isFirstFocus - True on the first focus after onOpen; false on subsequent focuses.
|
|
536
|
+
* @returns {boolean} Whether a focus() call succeeded.
|
|
537
|
+
*/
|
|
538
|
+
autoFocus(handle, isFirstFocus) {
|
|
539
|
+
const host = handle?.host;
|
|
540
|
+
if (host == null) return false;
|
|
541
|
+
|
|
542
|
+
if (!isFirstFocus) {
|
|
543
|
+
const last = handle.lastFocusedElement;
|
|
544
|
+
if (last != null && host.contains(last) && document.body.contains(last)) {
|
|
545
|
+
last.focus();
|
|
546
|
+
if (document.activeElement === last) return true;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const markedTarget = host.querySelector("[data-autofocus]");
|
|
551
|
+
if (markedTarget != null && !markedTarget.hasAttribute("disabled") && !markedTarget.hidden) {
|
|
552
|
+
markedTarget.focus();
|
|
553
|
+
if (document.activeElement === markedTarget) return true;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const candidates = host.querySelectorAll(
|
|
557
|
+
'input:not([disabled]),textarea:not([disabled]),select:not([disabled]),button:not([disabled]),[tabindex]:not([tabindex="-1"])'
|
|
558
|
+
);
|
|
559
|
+
for (const el of candidates) {
|
|
560
|
+
if (el.hidden) continue;
|
|
561
|
+
el.focus();
|
|
562
|
+
if (document.activeElement === el) return true;
|
|
563
|
+
}
|
|
564
|
+
return false;
|
|
565
|
+
}
|
|
524
566
|
}
|
|
525
567
|
|
|
526
568
|
const pageManager = new EstreUiPageManager();
|
|
@@ -99,6 +99,8 @@ class EstrePageHandle {
|
|
|
99
99
|
get isShowing() { return this.isOpened && this.#isShowing; }
|
|
100
100
|
#isFocused = false;
|
|
101
101
|
get isFocused() { return this.isShowing && this.#isFocused; }
|
|
102
|
+
#everFocused = false;
|
|
103
|
+
get everFocused() { return this.#everFocused; }
|
|
102
104
|
|
|
103
105
|
#isHiding = false;
|
|
104
106
|
get isHiding() { return this.#isHiding; }
|
|
@@ -123,6 +125,8 @@ class EstrePageHandle {
|
|
|
123
125
|
#isProcessing = f;
|
|
124
126
|
get isProcessing() { return this.#isProcessing; }
|
|
125
127
|
|
|
128
|
+
lastFocusedElement = null;
|
|
129
|
+
|
|
126
130
|
get mainArticle() { return this; }
|
|
127
131
|
|
|
128
132
|
|
|
@@ -381,7 +385,9 @@ class EstrePageHandle {
|
|
|
381
385
|
const task = this.hide();
|
|
382
386
|
return postAsyncQueue(async _ => {
|
|
383
387
|
await task;
|
|
384
|
-
|
|
388
|
+
const result = await this.onClose(isTermination, isOnRelease);
|
|
389
|
+
this.#isClosing = false;
|
|
390
|
+
return result;
|
|
385
391
|
});
|
|
386
392
|
} else return false;
|
|
387
393
|
}
|
|
@@ -416,10 +422,18 @@ class EstrePageHandle {
|
|
|
416
422
|
|
|
417
423
|
onFocus() {
|
|
418
424
|
if (!this.isFocused) {
|
|
425
|
+
const isFirstFocus = !this.#everFocused;
|
|
419
426
|
if (window.isDebug) console.log("[onFocus] " + this.sectionBound + " " + this.hostType + " " + this.pid);//, this.host);
|
|
420
427
|
this.#isFocused = true;
|
|
421
|
-
|
|
428
|
+
this.#everFocused = true;
|
|
429
|
+
const handled = this.handler?.onFocus?.(this, isFirstFocus);
|
|
422
430
|
if (this.intent?.onFocus != null) for (var item of this.intent.onFocus) if (item.from == this.hostType && !item.disabled) this.processAction(item);
|
|
431
|
+
if (handled === true) {
|
|
432
|
+
// Snapshot activeElement so a later refocus (e.g. background→foreground)
|
|
433
|
+
// can restore whatever the handler focused, even if focusin didn't record it.
|
|
434
|
+
const ae = document.activeElement;
|
|
435
|
+
if (ae != null && ae !== document.body && this.host?.contains(ae)) this.lastFocusedElement = ae;
|
|
436
|
+
} else pageManager.autoFocus?.(this, isFirstFocus);
|
|
423
437
|
return true;
|
|
424
438
|
} else return false;
|
|
425
439
|
}
|
|
@@ -451,7 +465,7 @@ class EstrePageHandle {
|
|
|
451
465
|
this.#isFocused = false;
|
|
452
466
|
if (window.isDebug) console.log("[onBlur] " + this.sectionBound + " " + this.hostType + " " + this.pid);//, this.host);
|
|
453
467
|
if (this.intent?.onBlur != null) for (var item of this.intent.onBlur) if (item.from == this.hostType && !item.disabled) await this.processAction(item);
|
|
454
|
-
|
|
468
|
+
await this.handler?.onBlur?.(this, this.isClosing);
|
|
455
469
|
return true;
|
|
456
470
|
} else return false;
|
|
457
471
|
}
|
|
@@ -474,6 +488,7 @@ class EstrePageHandle {
|
|
|
474
488
|
postQueue(_ => pageManager.bringPage(pid));
|
|
475
489
|
}
|
|
476
490
|
}
|
|
491
|
+
this.#isHiding = false;
|
|
477
492
|
return true;
|
|
478
493
|
} else return false;
|
|
479
494
|
}
|
|
@@ -481,6 +496,8 @@ class EstrePageHandle {
|
|
|
481
496
|
async onClose(isTermination = false, isOnRelease = false) {
|
|
482
497
|
if (this.isOpened && (isOnRelease || !this.isStatic)) {
|
|
483
498
|
this.#isOpened = false;
|
|
499
|
+
this.#everFocused = false;
|
|
500
|
+
this.lastFocusedElement = null;
|
|
484
501
|
if (window.isDebug) console.log("[onClose] " + this.sectionBound + " " + this.hostType + " " + this.pid);//, this.host);
|
|
485
502
|
if (this.intent?.onClose != null) for (var item of this.intent.onClose) if (item.from == this.hostType && !item.disabled) await this.processAction(item);
|
|
486
503
|
if (this.handler?.onClose != null) await this.handler.onClose(this);
|
|
@@ -2975,6 +2992,7 @@ class EstreAlertDialogPageHandler extends EstreDialogPageHandler {
|
|
|
2975
2992
|
|
|
2976
2993
|
onFocus(handle) {
|
|
2977
2994
|
this.$confirm.focus();
|
|
2995
|
+
return true;
|
|
2978
2996
|
}
|
|
2979
2997
|
}
|
|
2980
2998
|
|
|
@@ -3022,6 +3040,7 @@ class EstreConfirmDialogPageHandler extends EstreDialogPageHandler {
|
|
|
3022
3040
|
|
|
3023
3041
|
onFocus(handle) {
|
|
3024
3042
|
this.$negative.focus();
|
|
3043
|
+
return true;
|
|
3025
3044
|
}
|
|
3026
3045
|
}
|
|
3027
3046
|
|
|
@@ -3081,6 +3100,7 @@ class EstrePromptDialogPageHandler extends EstreDialogPageHandler {
|
|
|
3081
3100
|
|
|
3082
3101
|
onFocus(handle) {
|
|
3083
3102
|
this.$input.focus();
|
|
3103
|
+
return true;
|
|
3084
3104
|
}
|
|
3085
3105
|
}
|
|
3086
3106
|
|
|
@@ -3110,6 +3130,7 @@ class EstreOptionDialogPageHandler extends EstreDialogPageHandler {
|
|
|
3110
3130
|
|
|
3111
3131
|
onFocus(handle) {
|
|
3112
3132
|
this.$optionItems[0]?.focus();
|
|
3133
|
+
return true;
|
|
3113
3134
|
}
|
|
3114
3135
|
}
|
|
3115
3136
|
|
|
@@ -3213,6 +3234,7 @@ class EstreSelectionDialogPageHandler extends EstreDialogPageHandler {
|
|
|
3213
3234
|
|
|
3214
3235
|
onFocus(handle) {
|
|
3215
3236
|
this.$confirm.focus();
|
|
3237
|
+
return true;
|
|
3216
3238
|
}
|
|
3217
3239
|
|
|
3218
3240
|
checkValidSelectAction(handle, handler, index, value, checked) {
|
|
@@ -3291,6 +3313,7 @@ class EstreDialsDialogPageHandler extends EstreDialogPageHandler {
|
|
|
3291
3313
|
|
|
3292
3314
|
onFocus(handle) {
|
|
3293
3315
|
this.$confirm.focus();
|
|
3316
|
+
return true;
|
|
3294
3317
|
}
|
|
3295
3318
|
|
|
3296
3319
|
onClose(handle) {
|
package/serviceWorker.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const INSTALLATION_VERSION_NAME = "1.2.
|
|
1
|
+
const INSTALLATION_VERSION_NAME = "1.2.6-r20260422";
|
|
2
2
|
// ^^ Use for check new update "Native application(webview) version(or Android/iOS version combo) - PWA release version"
|
|
3
3
|
// ex) "1.0.1/1.0.0-r20251101k"
|
|
4
4
|
|
|
@@ -21,7 +21,7 @@ const INSTALLATION_FILE_LIST = [
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
// Common files cache - Be changes some time but, well not changed very often
|
|
24
|
-
const CACHE_NAME_COMMON_FILES = "common-files-cache-v1-
|
|
24
|
+
const CACHE_NAME_COMMON_FILES = "common-files-cache-v1-20260422";
|
|
25
25
|
|
|
26
26
|
const COMMON_FILES_TO_CACHE = [
|
|
27
27
|
"./",
|