@takagaki/cortex-decisions-viewer 0.12.27 → 0.12.28
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/dist/render.js +141 -8
- package/package.json +1 -1
package/dist/render.js
CHANGED
|
@@ -2167,6 +2167,10 @@ a:hover { text-decoration: underline; }
|
|
|
2167
2167
|
}
|
|
2168
2168
|
.site-title { font-size: 18px; font-weight: 700; color: var(--text); }
|
|
2169
2169
|
.site-title:hover { text-decoration: none; color: var(--accent); }
|
|
2170
|
+
/* ポータル配下で開いているとき(#379)、案件名はポータルの案件一覧(同一オリジンの /)へ
|
|
2171
|
+
戻るリンクになる。**文言は増やさない**(案件名そのものがリンク)ので、押せることは
|
|
2172
|
+
ホバーの下線で示す。丸(アイコン操作)とは別の口 */
|
|
2173
|
+
.site-title.site-title-portal:hover { text-decoration: underline; text-underline-offset: 3px; }
|
|
2170
2174
|
/* ---- 案件アイコン(#348)----
|
|
2171
2175
|
**ヘッダーの案件名の左に丸を置く。** ヘッダーは全タブ共通なので、
|
|
2172
2176
|
どのタブを開いていても同じ場所に居る。0.12.24 で Home 本文に置いた
|
|
@@ -2219,6 +2223,12 @@ button.site-icon[aria-expanded="true"] .site-icon-hover { opacity: 1; }
|
|
|
2219
2223
|
——押せない丸に指差しカーソルやリングを出すのが、画面の嘘のうちで一番たちが悪い */
|
|
2220
2224
|
button.site-icon { cursor: pointer; }
|
|
2221
2225
|
button.site-icon:hover, button.site-icon[aria-expanded="true"] { box-shadow: 0 0 0 2px var(--accent); }
|
|
2226
|
+
/* **保存中(#378)は丸の上に .busy-overlay(共通部品)が載る。** その間はホバーの「変更」「+」
|
|
2227
|
+
もリングも出さない(スピナーと重なって読めない・押せるように見せない)。
|
|
2228
|
+
ホバー規則と同じ強さなので、**この行はホバー規則より後に置く** */
|
|
2229
|
+
button.site-icon[aria-busy="true"] { cursor: wait; box-shadow: none; }
|
|
2230
|
+
button.site-icon[aria-busy="true"] .site-icon-hover,
|
|
2231
|
+
button.site-icon[aria-busy="true"] .site-icon-plus { opacity: 0; }
|
|
2222
2232
|
.site-icon-img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block; }
|
|
2223
2233
|
/* 丸を押したときだけ出るメニュー。スロットを基準に浮かせる(ヘッダーは sticky なので
|
|
2224
2234
|
ページを送っても位置がずれない) */
|
|
@@ -3267,6 +3277,28 @@ button.ops-card-btn:disabled { opacity: .6; cursor: default; }
|
|
|
3267
3277
|
.toast-show { opacity: 1; }
|
|
3268
3278
|
.toast-error { background: #b91c1c; }
|
|
3269
3279
|
|
|
3280
|
+
/* 反映待ちのオーバーレイ+スピナー(#378・共通部品。JS の beginOverlaySpinner が載せる)。
|
|
3281
|
+
押した瞬間から結果が返るまで、**押した場所そのものの上に**半透明の白(60%)と
|
|
3282
|
+
ぐるぐるを重ねる——押した反応が無いように見えて押し直す(二重送信)のを止める。
|
|
3283
|
+
載せ先の大きさは変えない(absolute で内側に重ね、角丸は載せ先から継ぐ)。
|
|
3284
|
+
いまは案件アイコンの丸にだけ使う。操作タブの保存にも同じ部品を使える */
|
|
3285
|
+
.busy-overlay {
|
|
3286
|
+
position: absolute; top: 0; left: 0; right: 0; bottom: 0; border-radius: inherit;
|
|
3287
|
+
display: flex; align-items: center; justify-content: center;
|
|
3288
|
+
background: rgba(255, 255, 255, .6); cursor: wait; z-index: 2;
|
|
3289
|
+
}
|
|
3290
|
+
.busy-spinner {
|
|
3291
|
+
width: 18px; height: 18px; border-radius: 50%;
|
|
3292
|
+
border: 2px solid var(--accent-weak); border-top-color: var(--accent);
|
|
3293
|
+
animation: busy-spin .8s linear infinite;
|
|
3294
|
+
}
|
|
3295
|
+
@keyframes busy-spin { to { transform: rotate(360deg); } }
|
|
3296
|
+
/* 読み上げ用の語(「保存中…」)。目には見せない(文言を増やさない) */
|
|
3297
|
+
.busy-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
|
3298
|
+
/* 動きを減らす設定でも止めはしない——止めると「保存中」と「固まった」の区別が付かない。
|
|
3299
|
+
回転を半分の速さに落とすだけ */
|
|
3300
|
+
@media (prefers-reduced-motion: reduce) { .busy-spinner { animation-duration: 1.6s; } }
|
|
3301
|
+
|
|
3270
3302
|
.site-footer { max-width: 960px; margin: 0 auto; padding: 0 20px 40px; color: var(--muted); font-size: 12px; }
|
|
3271
3303
|
|
|
3272
3304
|
@media (max-width: 600px) {
|
|
@@ -4404,6 +4436,27 @@ ${deriveCapTile.toString()}
|
|
|
4404
4436
|
// document に付ける開閉のリスナーは1組だけ。描き直しのたびに足すと Escape 1回で
|
|
4405
4437
|
// 同じ処理が何度も走る(見た目は同じなので気づけない)
|
|
4406
4438
|
var siteIconGlobalBound = false;
|
|
4439
|
+
// 保存中か(画像を選ぶ/変える/外す の3段が走っている間)。**DOM ではなくこの変数が正本**
|
|
4440
|
+
// (#378)。描き直しで丸が作り直されても、renderSiteIcon がこれを見てオーバーレイを付け直す。
|
|
4441
|
+
// setProjectIcon / removeProjectIcon の入口で見て、走っている間の2回目を捨てる(二重送信防止)
|
|
4442
|
+
var siteIconSaving = false;
|
|
4443
|
+
// いま丸に載っているオーバーレイの外し方(無ければ null)
|
|
4444
|
+
var siteIconBusyEnd = null;
|
|
4445
|
+
|
|
4446
|
+
/**
|
|
4447
|
+
* 丸の保存中の見た目を付ける/外す。丸は disabled にして押せなくする
|
|
4448
|
+
* (メニューの開閉もここで止まる。丸のクリックは siteIconSaving でも見る)
|
|
4449
|
+
*/
|
|
4450
|
+
function setSiteIconBusy(circle, on) {
|
|
4451
|
+
if (siteIconBusyEnd) { siteIconBusyEnd(); siteIconBusyEnd = null; }
|
|
4452
|
+
if (!circle) return;
|
|
4453
|
+
if (on) {
|
|
4454
|
+
siteIconBusyEnd = beginOverlaySpinner(circle, "保存中…");
|
|
4455
|
+
circle.disabled = true;
|
|
4456
|
+
} else {
|
|
4457
|
+
circle.disabled = false;
|
|
4458
|
+
}
|
|
4459
|
+
}
|
|
4407
4460
|
|
|
4408
4461
|
/**
|
|
4409
4462
|
* 案件名の頭文字1字。**ポータルの ProjectIcon.tsx(initialOf)と同じ取り方**にする
|
|
@@ -4486,8 +4539,16 @@ ${deriveCapTile.toString()}
|
|
|
4486
4539
|
}
|
|
4487
4540
|
slot.appendChild(circle);
|
|
4488
4541
|
if (!intake) return;
|
|
4489
|
-
|
|
4490
|
-
|
|
4542
|
+
// 保存中に描き直されても、オーバーレイと押せない状態を引き継ぐ(正本は siteIconSaving)。
|
|
4543
|
+
// いまの呼び出し経路(起動時1回・ui.busy(false) の後の redraw)では保存中に来ないので
|
|
4544
|
+
// 防御。将来タブ切り替え等でヘッダーを描き直すようになっても、ぐるぐるが消えない
|
|
4545
|
+
if (siteIconSaving) setSiteIconBusy(circle, true);
|
|
4546
|
+
|
|
4547
|
+
circle.addEventListener("click", function () {
|
|
4548
|
+
// 保存中は丸が disabled なので click 自体が飛ばない(ブラウザの既定)。これは防御
|
|
4549
|
+
if (siteIconSaving) return;
|
|
4550
|
+
setSiteIconMenu(!siteIconMenuOpen);
|
|
4551
|
+
});
|
|
4491
4552
|
|
|
4492
4553
|
var menu = el("div", { class: "site-icon-menu", role: "menu" });
|
|
4493
4554
|
if (!siteIconMenuOpen) menu.setAttribute("hidden", "");
|
|
@@ -4497,6 +4558,11 @@ ${deriveCapTile.toString()}
|
|
|
4497
4558
|
// 丸へ戻す(キーボードの人が位置を失わない。Escape は document 側なので効く)
|
|
4498
4559
|
redraw: function () {
|
|
4499
4560
|
renderSiteIcon();
|
|
4561
|
+
ui.refocus();
|
|
4562
|
+
},
|
|
4563
|
+
// 丸へフォーカスを戻す。失敗のあとも呼ぶ——押していた項目は disabled にした瞬間に
|
|
4564
|
+
// フォーカスを失って body に落ちるので、戻さないとキーボードの人がやり直す位置を失う
|
|
4565
|
+
refocus: function () {
|
|
4500
4566
|
var s = document.getElementById("site-icon-slot");
|
|
4501
4567
|
var c = s && s.querySelector("button.site-icon");
|
|
4502
4568
|
if (c && typeof c.focus === "function") c.focus();
|
|
@@ -4504,10 +4570,14 @@ ${deriveCapTile.toString()}
|
|
|
4504
4570
|
input: null,
|
|
4505
4571
|
buttons: [],
|
|
4506
4572
|
};
|
|
4507
|
-
//
|
|
4573
|
+
// 送信中は全部を止める(#378: 丸にオーバーレイ+スピナー・丸とメニューを押せなくする)。
|
|
4574
|
+
// **元から押せない項目は押せないまま戻す**——
|
|
4508
4575
|
// 未設定の「画像を外す」が、失敗して戻ったときだけ押せるようになるのを防ぐ
|
|
4509
|
-
ui.
|
|
4576
|
+
ui.busy = function (flag) {
|
|
4577
|
+
siteIconSaving = !!flag;
|
|
4510
4578
|
ui.buttons.forEach(function (b) { b.disabled = flag ? true : !!b.lockedOff; });
|
|
4579
|
+
var s = document.getElementById("site-icon-slot");
|
|
4580
|
+
setSiteIconBusy(s && s.querySelector("button.site-icon"), siteIconSaving);
|
|
4511
4581
|
};
|
|
4512
4582
|
|
|
4513
4583
|
var input = el("input", { type: "file", accept: "image/png,image/jpeg,image/webp", hidden: "" });
|
|
@@ -4559,7 +4629,9 @@ ${deriveCapTile.toString()}
|
|
|
4559
4629
|
* ものを送ると、1MB を上げきってから「大きすぎます」と言うことになる
|
|
4560
4630
|
*/
|
|
4561
4631
|
async function setProjectIcon(file, ui) {
|
|
4562
|
-
|
|
4632
|
+
// 走っている間の2回目は捨てる(丸もメニューも止めてあるが、正本はこの変数)
|
|
4633
|
+
if (siteIconSaving) return;
|
|
4634
|
+
function fail(msg) { showToast("✗ " + msg, true); ui.busy(false); ui.refocus(); }
|
|
4563
4635
|
// 画面側の門。**サーバ側と同じ文言**で、送る前に断る(fail で「✗ 」を揃える)
|
|
4564
4636
|
if (!projectIconExtOfName(file.name)) return fail("PNG・JPEG・WebP の画像を選んでください");
|
|
4565
4637
|
if (file.size > PROJECT_ICON_MAX_BYTES) return fail("1MB までの画像を選んでください");
|
|
@@ -4567,7 +4639,8 @@ ${deriveCapTile.toString()}
|
|
|
4567
4639
|
|
|
4568
4640
|
var intake = SITE.intake;
|
|
4569
4641
|
var ctype = file.type || "application/octet-stream";
|
|
4570
|
-
|
|
4642
|
+
// 押した瞬間からぐるぐる(#378)。成功なら redraw で新しい丸に、失敗なら fail で元の丸に戻る
|
|
4643
|
+
ui.busy(true);
|
|
4571
4644
|
|
|
4572
4645
|
var pres;
|
|
4573
4646
|
try {
|
|
@@ -4602,6 +4675,7 @@ ${deriveCapTile.toString()}
|
|
|
4602
4675
|
// **同じファイルをもう一度選べるようにする。** value を戻さないと、選び直しても
|
|
4603
4676
|
// change が飛ばない(形式で断られた直後に選び直す経路がここで死ぬ)
|
|
4604
4677
|
if (ui.input) ui.input.value = "";
|
|
4678
|
+
ui.busy(false);
|
|
4605
4679
|
// 丸がその場で変わるので、メニューは畳んでよい(結果はトーストが伝える)
|
|
4606
4680
|
setSiteIconMenu(false);
|
|
4607
4681
|
ui.redraw();
|
|
@@ -4610,14 +4684,17 @@ ${deriveCapTile.toString()}
|
|
|
4610
4684
|
|
|
4611
4685
|
/** 画像を外す。**確認は出さない**——押し間違えても、もう一度選び直せば戻る操作 */
|
|
4612
4686
|
async function removeProjectIcon(ui) {
|
|
4613
|
-
|
|
4614
|
-
|
|
4687
|
+
// 保存中はメニューの項目が disabled なので普通は来ない(setProjectIcon と同じ防御)
|
|
4688
|
+
if (siteIconSaving) return;
|
|
4689
|
+
function fail(msg) { showToast("✗ " + msg, true); ui.busy(false); ui.refocus(); }
|
|
4690
|
+
ui.busy(true);
|
|
4615
4691
|
var res;
|
|
4616
4692
|
try {
|
|
4617
4693
|
res = await intakeJson(callIntake("project-icon-remove", { projectKey: SITE.intake.projectKey }));
|
|
4618
4694
|
} catch (net) { return fail("サーバーに接続できませんでした。通信環境を確認して、もう一度お試しください。"); }
|
|
4619
4695
|
if (!res.ok) return fail((res.j && res.j.message) || "外せませんでした");
|
|
4620
4696
|
rememberIconPending("remove", "");
|
|
4697
|
+
ui.busy(false);
|
|
4621
4698
|
setSiteIconMenu(false);
|
|
4622
4699
|
ui.redraw();
|
|
4623
4700
|
showToast("外しました", false);
|
|
@@ -9380,6 +9457,48 @@ ${deriveCapTile.toString()}
|
|
|
9380
9457
|
};
|
|
9381
9458
|
}
|
|
9382
9459
|
|
|
9460
|
+
/**
|
|
9461
|
+
* 反映待ちのオーバーレイ+スピナー(#378・共通部品)。
|
|
9462
|
+
*
|
|
9463
|
+
* host の**上に**半透明の白(60%)とぐるぐるを重ねる。host の大きさは変えない。
|
|
9464
|
+
* 角丸は host から継ぐので、丸(案件アイコン)でも四角(操作タブのカード・ボタン)でも
|
|
9465
|
+
* 使える。beginBusy が「語を差し替える」形なのに対し、こちらは**語を置けない場所
|
|
9466
|
+
* (画像だけの丸など)**向け。
|
|
9467
|
+
*
|
|
9468
|
+
* **載せ先が static でも壊れない。** オーバーレイは absolute なので、載せ先に
|
|
9469
|
+
* position が無いと**別の祖先いっぱいに広がる**(黙って壊れる)。その場合だけ
|
|
9470
|
+
* relative を入れ、外すときに戻す(案件アイコンの丸は元から relative なので触らない)。
|
|
9471
|
+
*
|
|
9472
|
+
* 支援技術には host の aria-busy で「処理中」を伝える。label は目に見えない語として
|
|
9473
|
+
* 置くだけ(挿入と同時に中身を入れたライブリージョンは読まれないことが多い)。
|
|
9474
|
+
*
|
|
9475
|
+
* @returns 外す関数。beginBusy と同じく**何度呼んでも1回しか効かない**
|
|
9476
|
+
*/
|
|
9477
|
+
function beginOverlaySpinner(host, label) {
|
|
9478
|
+
if (!host) return function () {};
|
|
9479
|
+
var madeRelative = false;
|
|
9480
|
+
try {
|
|
9481
|
+
if (window.getComputedStyle(host).position === "static") {
|
|
9482
|
+
host.style.position = "relative";
|
|
9483
|
+
madeRelative = true;
|
|
9484
|
+
}
|
|
9485
|
+
} catch (e) {}
|
|
9486
|
+
var ov = el("span", { class: "busy-overlay" }, [
|
|
9487
|
+
el("span", { class: "busy-spinner", "aria-hidden": "true" }),
|
|
9488
|
+
el("span", { class: "busy-label", text: label || "処理中…" }),
|
|
9489
|
+
]);
|
|
9490
|
+
host.appendChild(ov);
|
|
9491
|
+
host.setAttribute("aria-busy", "true");
|
|
9492
|
+
var used = false;
|
|
9493
|
+
return function endOverlaySpinner() {
|
|
9494
|
+
if (used) return;
|
|
9495
|
+
used = true;
|
|
9496
|
+
if (ov.parentNode) ov.parentNode.removeChild(ov);
|
|
9497
|
+
host.removeAttribute("aria-busy");
|
|
9498
|
+
if (madeRelative) host.style.position = "";
|
|
9499
|
+
};
|
|
9500
|
+
}
|
|
9501
|
+
|
|
9383
9502
|
/**
|
|
9384
9503
|
* サーバへ設定変更を送る。**一覧は書き換えない**
|
|
9385
9504
|
* (接続状況は夜間バッチのスナップショットなので、「もう効いている」と誤解させないため)。
|
|
@@ -13485,6 +13604,20 @@ ${deriveCapTile.toString()}
|
|
|
13485
13604
|
}
|
|
13486
13605
|
|
|
13487
13606
|
window.addEventListener("popstate", route);
|
|
13607
|
+
/**
|
|
13608
|
+
* ヘッダーの案件名(#379)。**ポータル配下(/p/<key>/ の下)で開いているときだけ**、
|
|
13609
|
+
* ポータルの案件一覧(同一オリジンの /)へ戻るリンクにする。
|
|
13610
|
+
* Basic 認証の Amplify で開いているときは戻る先が無いので、これまでどおり(href="?")。
|
|
13611
|
+
* 文言は増やさない——案件名そのものがリンクで、説明は title 属性だけ。
|
|
13612
|
+
* 丸(アイコン操作)とは別の口なので、renderSiteIcon には触らない
|
|
13613
|
+
*/
|
|
13614
|
+
(function linkTitleToPortal() {
|
|
13615
|
+
var a = document.getElementById("home-link");
|
|
13616
|
+
if (!a || !/^\\/p\\/[^/]+\\//.test(location.pathname)) return;
|
|
13617
|
+
a.setAttribute("href", "/");
|
|
13618
|
+
a.setAttribute("title", "案件一覧へ");
|
|
13619
|
+
a.classList.add("site-title-portal");
|
|
13620
|
+
})();
|
|
13488
13621
|
// ヘッダーの案件アイコン(#348)は #app の外なので、**起動時に1回だけ描く**。
|
|
13489
13622
|
// route() はタブを替えるたびに走るが、ヘッダーは描き替えない——描き直すと
|
|
13490
13623
|
// 保存直後の mirror とメニューの開閉が、タブを移るたびに畳まれる
|
package/package.json
CHANGED