@nuniapp/widget 0.1.7 → 0.1.8
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/index.d.ts +7 -2
- package/dist/index.js +189 -16
- package/dist/nuni.global.js +30 -9
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ declare const EN: {
|
|
|
30
30
|
noOpenKey: string;
|
|
31
31
|
noResolved: string;
|
|
32
32
|
notFoundHere: string;
|
|
33
|
+
searchComments: string;
|
|
34
|
+
noMatches: string;
|
|
33
35
|
otherPages: string;
|
|
34
36
|
openOnPage: string;
|
|
35
37
|
replyCount: {
|
|
@@ -105,7 +107,7 @@ type MessageKey = keyof typeof EN;
|
|
|
105
107
|
type Messages = { [K in MessageKey]: string | Plural; };
|
|
106
108
|
//#endregion
|
|
107
109
|
//#region src/config.d.ts
|
|
108
|
-
type Position = "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
110
|
+
type Position = "bottom-right" | "bottom-center" | "bottom-left" | "top-right" | "top-center" | "top-left";
|
|
109
111
|
type Theme = "auto" | "light" | "dark";
|
|
110
112
|
/** Built-in page keys, for when `getPageKey` is not set. */
|
|
111
113
|
type PageKey = "path" | "path+search" | "path+hash";
|
|
@@ -125,7 +127,10 @@ interface NuniOptions {
|
|
|
125
127
|
* the path (default), the path and query string, or the path and hash.
|
|
126
128
|
*/
|
|
127
129
|
pageKey?: PageKey;
|
|
128
|
-
/**
|
|
130
|
+
/**
|
|
131
|
+
* Where the toolbar starts. Default "bottom-right". Visitors can drag it to
|
|
132
|
+
* another corner or edge middle, and their choice is remembered.
|
|
133
|
+
*/
|
|
129
134
|
position?: Position;
|
|
130
135
|
/** Brand color for buttons, pins and highlights (any CSS color). */
|
|
131
136
|
accentColor?: string;
|
package/dist/index.js
CHANGED
|
@@ -165,7 +165,7 @@ function buildCommentPrompt(comment, options = {}) {
|
|
|
165
165
|
}
|
|
166
166
|
//#endregion
|
|
167
167
|
//#region src/config.ts
|
|
168
|
-
const VERSION = "0.1.
|
|
168
|
+
const VERSION = "0.1.8";
|
|
169
169
|
function resolveConfig(options) {
|
|
170
170
|
return {
|
|
171
171
|
project: options.project,
|
|
@@ -191,8 +191,10 @@ function resolveConfig(options) {
|
|
|
191
191
|
}
|
|
192
192
|
const POSITIONS = [
|
|
193
193
|
"bottom-right",
|
|
194
|
+
"bottom-center",
|
|
194
195
|
"bottom-left",
|
|
195
196
|
"top-right",
|
|
197
|
+
"top-center",
|
|
196
198
|
"top-left"
|
|
197
199
|
];
|
|
198
200
|
const THEMES = [
|
|
@@ -5555,6 +5557,8 @@ const EN = {
|
|
|
5555
5557
|
noOpenKey: "No open comments on this page. Press {key} to add one.",
|
|
5556
5558
|
noResolved: "Nothing resolved yet.",
|
|
5557
5559
|
notFoundHere: "Couldn't find on this page",
|
|
5560
|
+
searchComments: "Search comments",
|
|
5561
|
+
noMatches: "No comments match “{query}”.",
|
|
5558
5562
|
otherPages: "Other pages",
|
|
5559
5563
|
openOnPage: "{count} open",
|
|
5560
5564
|
replyCount: {
|
|
@@ -5690,6 +5694,7 @@ const ICONS = {
|
|
|
5690
5694
|
smile: s("<circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M8 14s1.5 2 4 2 4-2 4-2M9 9h.01M15 9h.01\"/>"),
|
|
5691
5695
|
user: s("<circle cx=\"12\" cy=\"8\" r=\"4\"/><path d=\"M4 21a8 8 0 0 1 16 0\"/>"),
|
|
5692
5696
|
arrowUp: s("<path d=\"M12 19V5M5 12l7-7 7 7\"/>"),
|
|
5697
|
+
search: s("<circle cx=\"11\" cy=\"11\" r=\"7\"/><path d=\"m21 21-4.3-4.3\"/>"),
|
|
5693
5698
|
target: s("<circle cx=\"12\" cy=\"12\" r=\"9\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>"),
|
|
5694
5699
|
github: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M12 .5a11.5 11.5 0 0 0-3.6 22.4c.6.1.8-.3.8-.6v-2c-3.2.7-3.9-1.5-3.9-1.5-.5-1.3-1.3-1.7-1.3-1.7-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.7-1.6-2.6-.3-5.3-1.3-5.3-5.7 0-1.3.5-2.3 1.2-3.1-.1-.3-.5-1.5.1-3.1 0 0 1-.3 3.2 1.2a11 11 0 0 1 5.8 0c2.2-1.5 3.2-1.2 3.2-1.2.6 1.6.2 2.8.1 3.1.8.8 1.2 1.9 1.2 3.1 0 4.4-2.7 5.4-5.3 5.7.4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A11.5 11.5 0 0 0 12 .5z"/></svg>`,
|
|
5695
5700
|
link: s("<path d=\"M15 3h6v6M10 14 21 3M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\"/>")
|
|
@@ -5945,7 +5950,11 @@ function write(key, value) {
|
|
|
5945
5950
|
const KEYS = {
|
|
5946
5951
|
name: "nuni:name",
|
|
5947
5952
|
secret: "nuni:author-secret",
|
|
5948
|
-
session: (project) => `nuni:session:${project}
|
|
5953
|
+
session: (project) => `nuni:session:${project}`,
|
|
5954
|
+
/** Where this visitor dragged the toolbar. */
|
|
5955
|
+
position: (project) => `nuni:position:${project}`,
|
|
5956
|
+
/** The comment this visitor opened last, marked in the panel. */
|
|
5957
|
+
lastViewed: (project) => `nuni:last-viewed:${project}`
|
|
5949
5958
|
};
|
|
5950
5959
|
//#endregion
|
|
5951
5960
|
//#region src/styles.ts
|
|
@@ -6022,7 +6031,18 @@ button { cursor: pointer; }
|
|
|
6022
6031
|
border: 1px solid var(--n-border);
|
|
6023
6032
|
box-shadow: var(--n-shadow);
|
|
6024
6033
|
pointer-events: auto;
|
|
6025
|
-
|
|
6034
|
+
cursor: grab;
|
|
6035
|
+
touch-action: none;
|
|
6036
|
+
user-select: none;
|
|
6037
|
+
-webkit-user-select: none;
|
|
6038
|
+
}
|
|
6039
|
+
.toolbar[data-dragging] {
|
|
6040
|
+
cursor: grabbing;
|
|
6041
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.08), 0 16px 40px rgba(0,0,0,0.2);
|
|
6042
|
+
}
|
|
6043
|
+
/* While dragging, the host covers the page so no move is lost to it. */
|
|
6044
|
+
:host([data-dragging]) { pointer-events: auto; cursor: grabbing; }
|
|
6045
|
+
:host([data-dragging]) .tb-btn { pointer-events: none; }
|
|
6026
6046
|
.tb-btn {
|
|
6027
6047
|
display: inline-flex;
|
|
6028
6048
|
align-items: center;
|
|
@@ -6179,7 +6199,6 @@ button { cursor: pointer; }
|
|
|
6179
6199
|
gap: 12px;
|
|
6180
6200
|
}
|
|
6181
6201
|
.confirm p { margin: 0; }
|
|
6182
|
-
.btn-danger-solid { background: var(--n-danger); color: #fff; border-color: transparent; }
|
|
6183
6202
|
|
|
6184
6203
|
/* Pins */
|
|
6185
6204
|
.pin {
|
|
@@ -6454,6 +6473,10 @@ textarea.field { resize: vertical; min-height: 72px; max-height: 240px; }
|
|
|
6454
6473
|
.btn-ghost { border-color: transparent; background: transparent; }
|
|
6455
6474
|
.btn-icon { width: 32px; padding: 0; justify-content: center; }
|
|
6456
6475
|
.btn-danger { color: var(--n-danger); }
|
|
6476
|
+
/* After .btn, which would otherwise paint over the red with the card's background. */
|
|
6477
|
+
.btn-danger-solid,
|
|
6478
|
+
.btn-danger-solid:hover { background: var(--n-danger); border-color: var(--n-danger); color: #fff; }
|
|
6479
|
+
.btn-danger-solid:hover { filter: brightness(1.08); }
|
|
6457
6480
|
.error { color: var(--n-danger); font-size: 12px; }
|
|
6458
6481
|
.divider { height: 1px; background: var(--n-border); }
|
|
6459
6482
|
|
|
@@ -6559,7 +6582,7 @@ textarea.field { resize: vertical; min-height: 72px; max-height: 240px; }
|
|
|
6559
6582
|
}
|
|
6560
6583
|
.panel-head { display: flex; align-items: center; gap: 8px; padding: 12px; border-bottom: 1px solid var(--n-border); }
|
|
6561
6584
|
.panel-title { font-weight: 650; flex: 1; }
|
|
6562
|
-
.tabs { display: flex; gap: 4px; padding:
|
|
6585
|
+
.tabs { display: flex; gap: 4px; padding: 0 12px; margin-top: 8px; }
|
|
6563
6586
|
.tab {
|
|
6564
6587
|
border: 0;
|
|
6565
6588
|
background: transparent;
|
|
@@ -6582,6 +6605,11 @@ textarea.field { resize: vertical; min-height: 72px; max-height: 240px; }
|
|
|
6582
6605
|
border-radius: 10px;
|
|
6583
6606
|
}
|
|
6584
6607
|
.item:hover { background: var(--n-bg-subtle); }
|
|
6608
|
+
/* The comment opened last, so it's easy to find your place again. */
|
|
6609
|
+
.item[aria-current="true"] { background: var(--n-accent-soft); box-shadow: inset 3px 0 0 var(--n-accent); }
|
|
6610
|
+
.panel-search { position: relative; padding: 0 12px; margin-top: 8px; }
|
|
6611
|
+
.panel-search .icon { position: absolute; left: 22px; top: 50%; transform: translateY(-50%); color: var(--n-fg-muted); pointer-events: none; }
|
|
6612
|
+
.panel-search .field { padding-left: 32px; }
|
|
6585
6613
|
.item-text { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; word-break: break-word; }
|
|
6586
6614
|
.section-label { padding: 12px 10px 4px; font-size: 11px; font-weight: 650; letter-spacing: 0.04em; text-transform: uppercase; color: var(--n-fg-muted); }
|
|
6587
6615
|
.empty { padding: 32px 16px; text-align: center; color: var(--n-fg-muted); }
|
|
@@ -6625,8 +6653,10 @@ a.item { color: inherit; text-decoration: none; }
|
|
|
6625
6653
|
* { transition: none !important; animation: none !important; }
|
|
6626
6654
|
}
|
|
6627
6655
|
|
|
6628
|
-
/* The toolbar's
|
|
6656
|
+
/* The toolbar's spot (the position option, or where it was dragged) */
|
|
6629
6657
|
:host([data-position$="left"]) .toolbar { right: auto; left: 16px; }
|
|
6658
|
+
:host([data-position$="center"]) .toolbar { left: 0; right: 0; width: max-content; margin-inline: auto; }
|
|
6659
|
+
:host([data-position="top-center"]) .pick-hint { top: auto; bottom: 12px; }
|
|
6630
6660
|
:host([data-position^="top"]) .toolbar { bottom: auto; top: 16px; }
|
|
6631
6661
|
:host([data-position$="left"]) .panel { right: auto; left: 12px; }
|
|
6632
6662
|
:host([data-position^="top"]) .panel { top: 72px; bottom: 12px; }
|
|
@@ -6738,6 +6768,8 @@ var NuniWidget = class {
|
|
|
6738
6768
|
_defineProperty(this, "picking", false);
|
|
6739
6769
|
_defineProperty(this, "panelOpen", false);
|
|
6740
6770
|
_defineProperty(this, "tab", "open");
|
|
6771
|
+
_defineProperty(this, "search", "");
|
|
6772
|
+
_defineProperty(this, "lastViewedId", void 0);
|
|
6741
6773
|
_defineProperty(this, "card", null);
|
|
6742
6774
|
_defineProperty(this, "activeId", null);
|
|
6743
6775
|
_defineProperty(this, "hoverEl", null);
|
|
@@ -6775,6 +6807,10 @@ var NuniWidget = class {
|
|
|
6775
6807
|
_defineProperty(this, "resolveTimer", 0);
|
|
6776
6808
|
_defineProperty(this, "shownPlacements", "");
|
|
6777
6809
|
_defineProperty(this, "i18n", void 0);
|
|
6810
|
+
_defineProperty(this, "position", void 0);
|
|
6811
|
+
_defineProperty(this, "toolbarDrag", null);
|
|
6812
|
+
_defineProperty(this, "endToolbarDrag", null);
|
|
6813
|
+
_defineProperty(this, "toolbarDropped", false);
|
|
6778
6814
|
_defineProperty(this, "isIgnored", (el) => el === this.host || this.host.contains(el));
|
|
6779
6815
|
_defineProperty(this, "onFrameKey", (e) => this.onKeyDown(e));
|
|
6780
6816
|
_defineProperty(this, "swallow", (e) => {
|
|
@@ -6854,6 +6890,8 @@ var NuniWidget = class {
|
|
|
6854
6890
|
}
|
|
6855
6891
|
this.secret = secret;
|
|
6856
6892
|
this.ownerToken = read(KEYS.session(config.project));
|
|
6893
|
+
this.lastViewedId = read(KEYS.lastViewed(config.project));
|
|
6894
|
+
this.position = oneOf(read(KEYS.position(config.project)) ?? void 0, POSITIONS, config.position);
|
|
6857
6895
|
this.host = document.createElement("div");
|
|
6858
6896
|
this.host.id = "nuni-root";
|
|
6859
6897
|
this.host.setAttribute("data-nuni", "");
|
|
@@ -6884,6 +6922,11 @@ var NuniWidget = class {
|
|
|
6884
6922
|
});
|
|
6885
6923
|
this.root.append(style, this.markLayer, this.pinLayer, this.overlayLayer, this.uiLayer, this.selectButton, this.live);
|
|
6886
6924
|
this.uiLayer.addEventListener("keydown", (e) => this.trapFocus(e));
|
|
6925
|
+
this.root.addEventListener("click", (e) => {
|
|
6926
|
+
if (!this.toolbarDropped) return;
|
|
6927
|
+
e.preventDefault();
|
|
6928
|
+
e.stopPropagation();
|
|
6929
|
+
}, true);
|
|
6887
6930
|
this.api = new NuniApi(config);
|
|
6888
6931
|
}
|
|
6889
6932
|
t(key, vars) {
|
|
@@ -6896,8 +6939,8 @@ var NuniWidget = class {
|
|
|
6896
6939
|
}
|
|
6897
6940
|
/** Position, theme, brand color and stacking order from the options. */
|
|
6898
6941
|
applyAppearance() {
|
|
6899
|
-
const {
|
|
6900
|
-
this.host.dataset.position = position;
|
|
6942
|
+
const { theme, accentColor, zIndex } = this.config;
|
|
6943
|
+
this.host.dataset.position = this.position;
|
|
6901
6944
|
this.host.dataset.theme = theme;
|
|
6902
6945
|
if (zIndex !== null) this.host.style.zIndex = String(zIndex);
|
|
6903
6946
|
const rgb = accentColor ? parseColor(accentColor) : null;
|
|
@@ -6944,6 +6987,7 @@ var NuniWidget = class {
|
|
|
6944
6987
|
if (e.composedPath().includes(this.host)) return;
|
|
6945
6988
|
this.pointerDown = true;
|
|
6946
6989
|
this.scheduleSelection();
|
|
6990
|
+
this.closeCardFromOutside(e);
|
|
6947
6991
|
};
|
|
6948
6992
|
const onUp = () => {
|
|
6949
6993
|
this.pointerDown = false;
|
|
@@ -6991,6 +7035,7 @@ var NuniWidget = class {
|
|
|
6991
7035
|
}
|
|
6992
7036
|
destroy() {
|
|
6993
7037
|
this.setPicking(false);
|
|
7038
|
+
this.endToolbarDrag?.();
|
|
6994
7039
|
this.pageUnsub?.();
|
|
6995
7040
|
this.sessionUnsub?.();
|
|
6996
7041
|
this.ownerDetailUnsub?.();
|
|
@@ -7515,7 +7560,10 @@ var NuniWidget = class {
|
|
|
7515
7560
|
if (this.confirming) this.answer(false);
|
|
7516
7561
|
else if (this.picking) this.setPicking(false);
|
|
7517
7562
|
else if (this.card) this.closeCard();
|
|
7518
|
-
else if (this.panelOpen
|
|
7563
|
+
else if (this.panelOpen && this.search) {
|
|
7564
|
+
this.search = "";
|
|
7565
|
+
this.render();
|
|
7566
|
+
} else if (this.panelOpen) this.togglePanel(false);
|
|
7519
7567
|
else return;
|
|
7520
7568
|
e.stopPropagation();
|
|
7521
7569
|
return;
|
|
@@ -7922,10 +7970,22 @@ var NuniWidget = class {
|
|
|
7922
7970
|
this.keepFocus();
|
|
7923
7971
|
this.focusNext = ".panel [role=\"tab\"][aria-selected=\"true\"]";
|
|
7924
7972
|
if (window.matchMedia(MOBILE_QUERY).matches) this.card = null;
|
|
7925
|
-
}
|
|
7973
|
+
} else this.search = "";
|
|
7926
7974
|
this.render();
|
|
7975
|
+
if (open) this.uiLayer.querySelector(".panel .item[aria-current=\"true\"]")?.scrollIntoView({ block: "nearest" });
|
|
7927
7976
|
if (!open && !this.card) this.restoreFocus();
|
|
7928
7977
|
}
|
|
7978
|
+
/**
|
|
7979
|
+
* A press on the page closes the open card, like Figma, unless the card
|
|
7980
|
+
* holds words that haven't been sent yet.
|
|
7981
|
+
*/
|
|
7982
|
+
closeCardFromOutside(e) {
|
|
7983
|
+
if (!this.card || this.picking || this.confirming || !e.isPrimary) return;
|
|
7984
|
+
if (onScrollbar(e)) return;
|
|
7985
|
+
if ([...this.uiLayer.querySelector("[data-card]")?.querySelectorAll("textarea") ?? []].some((t) => t.value.trim())) return;
|
|
7986
|
+
if (!this.panelOpen) this.returnFocus = null;
|
|
7987
|
+
this.closeCard();
|
|
7988
|
+
}
|
|
7929
7989
|
closeCard() {
|
|
7930
7990
|
const had = this.card !== null;
|
|
7931
7991
|
this.card = null;
|
|
@@ -7937,6 +7997,8 @@ var NuniWidget = class {
|
|
|
7937
7997
|
openThread(id) {
|
|
7938
7998
|
this.keepFocus();
|
|
7939
7999
|
this.activeId = id;
|
|
8000
|
+
this.lastViewedId = id;
|
|
8001
|
+
write(KEYS.lastViewed(this.config.project), id);
|
|
7940
8002
|
this.card = {
|
|
7941
8003
|
kind: "thread",
|
|
7942
8004
|
id
|
|
@@ -8035,10 +8097,14 @@ var NuniWidget = class {
|
|
|
8035
8097
|
}
|
|
8036
8098
|
renderToolbar() {
|
|
8037
8099
|
const open = this.comments.filter((c) => c.status === "open").length;
|
|
8100
|
+
const drag = this.toolbarDrag?.moving ? this.toolbarDrag : null;
|
|
8038
8101
|
return h("div", {
|
|
8039
8102
|
class: "toolbar",
|
|
8040
8103
|
role: "toolbar",
|
|
8041
|
-
"aria-label": this.t("toolbar")
|
|
8104
|
+
"aria-label": this.t("toolbar"),
|
|
8105
|
+
"data-dragging": !!drag,
|
|
8106
|
+
style: drag && `transform: translate(${drag.dx}px, ${drag.dy}px)`,
|
|
8107
|
+
onpointerdown: (e) => this.startToolbarDrag(e)
|
|
8042
8108
|
}, h("button", {
|
|
8043
8109
|
class: "tb-btn",
|
|
8044
8110
|
type: "button",
|
|
@@ -8057,16 +8123,94 @@ var NuniWidget = class {
|
|
|
8057
8123
|
onclick: () => this.togglePanel()
|
|
8058
8124
|
}, icon(ICONS.list), h("span", { class: "tb-count" }, String(open))));
|
|
8059
8125
|
}
|
|
8126
|
+
/**
|
|
8127
|
+
* Drag the toolbar anywhere; on release it glides to the nearest corner or
|
|
8128
|
+
* edge middle, which is remembered for this visitor.
|
|
8129
|
+
*/
|
|
8130
|
+
startToolbarDrag(e) {
|
|
8131
|
+
if (e.button !== 0 || !e.isPrimary) return;
|
|
8132
|
+
this.endToolbarDrag?.();
|
|
8133
|
+
const drag = {
|
|
8134
|
+
id: e.pointerId,
|
|
8135
|
+
x: e.clientX,
|
|
8136
|
+
y: e.clientY,
|
|
8137
|
+
dx: 0,
|
|
8138
|
+
dy: 0,
|
|
8139
|
+
moving: false
|
|
8140
|
+
};
|
|
8141
|
+
this.toolbarDrag = drag;
|
|
8142
|
+
const toolbar = () => this.uiLayer.querySelector(".toolbar");
|
|
8143
|
+
const onMove = (ev) => {
|
|
8144
|
+
if (ev.pointerId !== drag.id) return;
|
|
8145
|
+
drag.dx = ev.clientX - drag.x;
|
|
8146
|
+
drag.dy = ev.clientY - drag.y;
|
|
8147
|
+
if (!drag.moving && Math.hypot(drag.dx, drag.dy) < 5) return;
|
|
8148
|
+
if (!drag.moving) {
|
|
8149
|
+
drag.moving = true;
|
|
8150
|
+
this.host.dataset.dragging = "";
|
|
8151
|
+
toolbar()?.setAttribute("data-dragging", "");
|
|
8152
|
+
}
|
|
8153
|
+
ev.preventDefault();
|
|
8154
|
+
toolbar()?.style.setProperty("transform", `translate(${drag.dx}px, ${drag.dy}px)`);
|
|
8155
|
+
};
|
|
8156
|
+
const onUp = (ev) => {
|
|
8157
|
+
if (ev.pointerId !== drag.id) return;
|
|
8158
|
+
this.endToolbarDrag?.();
|
|
8159
|
+
if (drag.moving) this.dropToolbar(toolbar(), true);
|
|
8160
|
+
};
|
|
8161
|
+
const onCancel = (ev) => {
|
|
8162
|
+
if (ev instanceof PointerEvent && ev.pointerId !== drag.id) return;
|
|
8163
|
+
this.endToolbarDrag?.();
|
|
8164
|
+
if (drag.moving) this.dropToolbar(toolbar(), false);
|
|
8165
|
+
};
|
|
8166
|
+
window.addEventListener("pointermove", onMove, true);
|
|
8167
|
+
window.addEventListener("pointerup", onUp, true);
|
|
8168
|
+
window.addEventListener("pointercancel", onCancel, true);
|
|
8169
|
+
window.addEventListener("blur", onCancel);
|
|
8170
|
+
this.endToolbarDrag = () => {
|
|
8171
|
+
window.removeEventListener("pointermove", onMove, true);
|
|
8172
|
+
window.removeEventListener("pointerup", onUp, true);
|
|
8173
|
+
window.removeEventListener("pointercancel", onCancel, true);
|
|
8174
|
+
window.removeEventListener("blur", onCancel);
|
|
8175
|
+
delete this.host.dataset.dragging;
|
|
8176
|
+
this.toolbarDrag = null;
|
|
8177
|
+
this.endToolbarDrag = null;
|
|
8178
|
+
};
|
|
8179
|
+
}
|
|
8180
|
+
/** Glide the toolbar to the spot nearest where it was let go, or back. */
|
|
8181
|
+
dropToolbar(el, snap) {
|
|
8182
|
+
this.toolbarDropped = true;
|
|
8183
|
+
setTimeout(() => this.toolbarDropped = false);
|
|
8184
|
+
if (!el) return;
|
|
8185
|
+
const from = el.getBoundingClientRect();
|
|
8186
|
+
if (snap) {
|
|
8187
|
+
const position = nearestPosition(from.left + from.width / 2, from.top + from.height / 2);
|
|
8188
|
+
this.position = position;
|
|
8189
|
+
this.host.dataset.position = position;
|
|
8190
|
+
write(KEYS.position(this.config.project), position === this.config.position ? null : position);
|
|
8191
|
+
}
|
|
8192
|
+
el.removeAttribute("data-dragging");
|
|
8193
|
+
el.style.removeProperty("transform");
|
|
8194
|
+
const to = el.getBoundingClientRect();
|
|
8195
|
+
if (matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
8196
|
+
el.animate([{ transform: `translate(${from.left - to.left}px, ${from.top - to.top}px)` }, { transform: "none" }], {
|
|
8197
|
+
duration: 320,
|
|
8198
|
+
easing: "cubic-bezier(0.2, 0.9, 0.3, 1.15)"
|
|
8199
|
+
});
|
|
8200
|
+
}
|
|
8060
8201
|
renderPanel() {
|
|
8061
8202
|
const open = this.comments.filter((c) => c.status === "open");
|
|
8062
8203
|
const resolved = this.comments.filter((c) => c.status === "resolved");
|
|
8063
|
-
const
|
|
8204
|
+
const query = this.search.trim().toLocaleLowerCase();
|
|
8205
|
+
const matches = (...texts) => !query || texts.some((t) => t?.toLocaleLowerCase().includes(query));
|
|
8206
|
+
const list = (this.tab === "open" ? open : resolved).filter((c) => matches(c.body, c.authorName, c.anchor.quote?.exact));
|
|
8064
8207
|
const placed = list.filter((c) => this.placements.get(c._id)?.element);
|
|
8065
8208
|
const lost = list.filter((c) => !this.placements.get(c._id)?.element);
|
|
8066
|
-
const otherPages = this.pages.filter((p) => p.path !== this.pageKey);
|
|
8209
|
+
const otherPages = this.pages.filter((p) => p.path !== this.pageKey && matches(p.path));
|
|
8067
8210
|
const item = (c) => h("button", {
|
|
8068
8211
|
class: "item",
|
|
8069
8212
|
type: "button",
|
|
8213
|
+
"aria-current": c._id === this.lastViewedId ? "true" : null,
|
|
8070
8214
|
"data-focus-key": `item-${c._id}`,
|
|
8071
8215
|
onclick: () => this.focusComment(c._id)
|
|
8072
8216
|
}, h("div", { class: "row" }, h("span", {
|
|
@@ -8078,6 +8222,7 @@ var NuniWidget = class {
|
|
|
8078
8222
|
}, new URL(c.page.origin).host) : null), c.anchor.quote ? h("div", { class: "item-quote" }, c.anchor.quote.exact) : null, h("div", { class: "item-text" }, c.body));
|
|
8079
8223
|
const children = [];
|
|
8080
8224
|
if (!this.loaded) children.push(h("div", { class: "empty" }, this.t("loading")));
|
|
8225
|
+
else if (!list.length && query) children.push(h("div", { class: "empty" }, this.t("noMatches", { query: this.search.trim() })));
|
|
8081
8226
|
else if (!list.length) children.push(h("div", { class: "empty" }, this.tab === "open" ? this.withKey("noOpen", "noOpenKey") : this.t("noResolved")));
|
|
8082
8227
|
else {
|
|
8083
8228
|
children.push(...placed.map(item));
|
|
@@ -8105,7 +8250,19 @@ var NuniWidget = class {
|
|
|
8105
8250
|
type: "button",
|
|
8106
8251
|
"aria-label": this.t("close"),
|
|
8107
8252
|
onclick: () => this.togglePanel(false)
|
|
8108
|
-
}, icon(ICONS.close))), h("div", {
|
|
8253
|
+
}, icon(ICONS.close))), this.comments.length ? h("div", { class: "panel-search" }, icon(ICONS.search), h("input", {
|
|
8254
|
+
class: "field",
|
|
8255
|
+
type: "search",
|
|
8256
|
+
value: this.search,
|
|
8257
|
+
placeholder: this.t("searchComments"),
|
|
8258
|
+
"aria-label": this.t("searchComments"),
|
|
8259
|
+
"aria-controls": "nuni-panel-list",
|
|
8260
|
+
"data-focus-key": "panel-search",
|
|
8261
|
+
oninput: (e) => {
|
|
8262
|
+
this.search = e.target.value;
|
|
8263
|
+
this.render();
|
|
8264
|
+
}
|
|
8265
|
+
})) : null, h("div", {
|
|
8109
8266
|
class: "tabs",
|
|
8110
8267
|
role: "tablist",
|
|
8111
8268
|
"aria-label": this.t("comments"),
|
|
@@ -8543,8 +8700,8 @@ var NuniWidget = class {
|
|
|
8543
8700
|
top = point.y - 28;
|
|
8544
8701
|
if (left + width > vw - 12) left = point.x - width - 12;
|
|
8545
8702
|
} else {
|
|
8546
|
-
const { position } = this
|
|
8547
|
-
left = position.endsWith("left") ? 16 : vw - width - 16;
|
|
8703
|
+
const { position } = this;
|
|
8704
|
+
left = position.endsWith("left") ? 16 : position.endsWith("center") ? (vw - width) / 2 : vw - width - 16;
|
|
8548
8705
|
top = position.startsWith("top") ? 72 : vh - height - 72;
|
|
8549
8706
|
}
|
|
8550
8707
|
left = Math.max(12, Math.min(left, vw - width - 12));
|
|
@@ -8558,6 +8715,22 @@ var NuniWidget = class {
|
|
|
8558
8715
|
});
|
|
8559
8716
|
}
|
|
8560
8717
|
};
|
|
8718
|
+
/** The toolbar spot nearest a point: thirds across the screen, halves down. */
|
|
8719
|
+
function nearestPosition(x, y) {
|
|
8720
|
+
const width = window.innerWidth;
|
|
8721
|
+
const across = x < width / 3 ? "left" : x > width * 2 / 3 ? "right" : "center";
|
|
8722
|
+
return `${y < window.innerHeight / 2 ? "top" : "bottom"}-${across}`;
|
|
8723
|
+
}
|
|
8724
|
+
/** A press on a scrollbar, which scrolls rather than points at the page. */
|
|
8725
|
+
function onScrollbar(e) {
|
|
8726
|
+
const el = e.composedPath()[0];
|
|
8727
|
+
if (!(el instanceof Element)) return false;
|
|
8728
|
+
const root = document.documentElement;
|
|
8729
|
+
if (el === root) return e.clientX >= root.clientWidth || e.clientY >= root.clientHeight;
|
|
8730
|
+
if (!(el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth)) return false;
|
|
8731
|
+
const box = el.getBoundingClientRect();
|
|
8732
|
+
return e.clientX - box.left - el.clientLeft >= el.clientWidth || e.clientY - box.top - el.clientTop >= el.clientHeight;
|
|
8733
|
+
}
|
|
8561
8734
|
//#endregion
|
|
8562
8735
|
//#region src/mount.ts
|
|
8563
8736
|
/** Nuni's own requests are not the page's problem. */
|