@nivaro/react 0.3.33 → 0.3.34

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.
Files changed (29) hide show
  1. package/dist/full.css +1 -1
  2. package/dist/hooks/useSessionRecorder.js +1 -1
  3. package/dist/index.d.ts +79 -5
  4. package/dist/index.js +338 -329
  5. package/dist/shared/components/ItemEditForm.js +5494 -5302
  6. package/dist/shared/components/ProfileView.js +46 -44
  7. package/dist/shared/components/ReportView.js +208 -205
  8. package/dist/shared/components/SearchHighlight.js +30 -0
  9. package/dist/shared/components/TipLayer.js +39 -37
  10. package/dist/shared/components/item-edit/CatalogPickerField.js +1484 -1232
  11. package/dist/shared/components/item-edit/FieldAffordances.js +370 -203
  12. package/dist/shared/components/item-edit/FieldRow.js +396 -354
  13. package/dist/shared/components/item-edit/PersonalDefaultPill.js +50 -0
  14. package/dist/shared/components/item-edit/RelationCombobox.js +42 -41
  15. package/dist/shared/components/item-edit/catalog-search.js +34 -0
  16. package/dist/shared/components/item-edit/entry/useEntryKeyboard.js +34 -34
  17. package/dist/shared/components/item-edit/personal-default-checks.js +134 -0
  18. package/dist/shared/components/item-edit/usePersonalDefaults.js +358 -0
  19. package/dist/shared/components/layout-editor/inspector/BehaviourSettings.js +130 -111
  20. package/dist/shared/components/layout-editor/inspector/RulesTab.js +76 -75
  21. package/dist/shared/components/layout-editor/parity-keys.json.js +1 -1
  22. package/dist/shared/components/profile/MyDefaultsCard.js +667 -0
  23. package/dist/shared/components/profile/PersonProfile.js +42 -40
  24. package/dist/shared/components/profile/my-defaults-labels.js +76 -0
  25. package/dist/shared/components/profile/primitives.js +72 -67
  26. package/dist/shared/lib/personal-defaults.js +76 -0
  27. package/dist/shared/lib/request-gate.js +78 -76
  28. package/dist/shared/lib/search-text.js +111 -0
  29. package/package.json +1 -1
@@ -1,59 +1,61 @@
1
- import { jsx as o, jsxs as x, Fragment as $ } from "react/jsx-runtime";
2
- import { Copy as F, History as O, Eye as K, Sigma as P, ExternalLink as B, Lock as H, X as I } from "lucide-react";
3
- import { useContext as U, createContext as q, useState as C, useCallback as g, useRef as v, useEffect as E } from "react";
4
- import { createPortal as J } from "react-dom";
5
- import { toast as k } from "sonner";
6
- import { htmlToText as R } from "../../lib/sanitize-html.js";
7
- import { cn as A, formatRelative as T } from "../../lib/utils.js";
8
- const V = q(null), re = () => U(V), j = (e) => {
9
- if (e == null || e === "") return "empty";
10
- if (typeof e == "boolean") return e ? "Yes" : "No";
11
- if (typeof e == "object")
1
+ import { jsx as s, jsxs as y, Fragment as z } from "react/jsx-runtime";
2
+ import { Copy as Y, History as Q, Eye as Z, Sigma as ee, ExternalLink as te, UserRound as ne, UserRoundX as re, Lock as oe, X } from "lucide-react";
3
+ import { useContext as ae, createContext as se, useState as M, useCallback as F, useRef as j, useEffect as _, useLayoutEffect as le } from "react";
4
+ import { createPortal as ie } from "react-dom";
5
+ import { toast as h } from "sonner";
6
+ import { htmlToText as ce } from "../../lib/sanitize-html.js";
7
+ import { cn as q, formatRelative as I } from "../../lib/utils.js";
8
+ import { PERSONAL_DEFAULT_GHOST_BUTTON as de } from "./PersonalDefaultPill.js";
9
+ import { usePersonalDefaultsApi as ue } from "./usePersonalDefaults.js";
10
+ const me = se(null), Ce = () => ae(me), V = (t) => {
11
+ if (t == null || t === "") return "empty";
12
+ if (typeof t == "boolean") return t ? "Yes" : "No";
13
+ if (typeof t == "object")
12
14
  try {
13
- const s = JSON.stringify(e);
14
- return s.length > 80 ? `${s.slice(0, 80)}…` : s;
15
+ const a = JSON.stringify(t);
16
+ return a.length > 80 ? `${a.slice(0, 80)}…` : a;
15
17
  } catch {
16
- return String(e);
18
+ return String(t);
17
19
  }
18
- const n = String(e), t = /[<&]/.test(n) ? R(n).trim() : n;
19
- return t.length > 80 ? `${t.slice(0, 80)}…` : t;
20
+ const n = String(t), e = /[<&]/.test(n) ? ce(n).trim() : n;
21
+ return e.length > 80 ? `${e.slice(0, 80)}…` : e;
20
22
  };
21
- function oe({
22
- change: e,
23
+ function Se({
24
+ change: t,
23
25
  onDismiss: n,
24
- onTakeTheirs: t
26
+ onTakeTheirs: e
25
27
  }) {
26
- return e.conflict && t ? /* @__PURE__ */ x(
28
+ return t.conflict && e ? /* @__PURE__ */ y(
27
29
  "div",
28
30
  {
29
31
  "data-remote-change-ghost": !0,
30
32
  "data-remote-conflict": !0,
31
33
  className: "nvr-expand-in flex flex-wrap items-center gap-1.5 rounded-md border border-amber-300 bg-amber-50 px-2 py-1 text-[11.5px] text-amber-900 dark:border-amber-500/40 dark:bg-amber-500/10 dark:text-amber-100",
32
34
  children: [
33
- /* @__PURE__ */ o("span", { className: "h-1.5 w-1.5 shrink-0 animate-pulse rounded-full bg-amber-500" }),
34
- /* @__PURE__ */ x("span", { className: "min-w-0 flex-1", children: [
35
- e.by ?? "Someone",
35
+ /* @__PURE__ */ s("span", { className: "h-1.5 w-1.5 shrink-0 animate-pulse rounded-full bg-amber-500" }),
36
+ /* @__PURE__ */ y("span", { className: "min-w-0 flex-1", children: [
37
+ t.by ?? "Someone",
36
38
  " changed this to",
37
39
  " ",
38
- /* @__PURE__ */ o("span", { className: "font-medium", children: j(e.theirs) }),
40
+ /* @__PURE__ */ s("span", { className: "font-medium", children: V(t.theirs) }),
39
41
  " while you were editing",
40
- /* @__PURE__ */ x("span", { className: "text-amber-700/80 dark:text-amber-200/70", children: [
42
+ /* @__PURE__ */ y("span", { className: "text-amber-700/80 dark:text-amber-200/70", children: [
41
43
  " ",
42
44
  "· ",
43
- T(e.at)
45
+ I(t.at)
44
46
  ] })
45
47
  ] }),
46
- /* @__PURE__ */ o(
48
+ /* @__PURE__ */ s(
47
49
  "button",
48
50
  {
49
51
  type: "button",
50
- onClick: t,
52
+ onClick: e,
51
53
  "data-remote-take-theirs": !0,
52
54
  className: "shrink-0 rounded border border-amber-400 bg-white px-1.5 py-0.5 text-[10.5px] font-medium text-amber-900 hover:bg-amber-100 dark:bg-transparent dark:text-amber-100 dark:hover:bg-amber-500/20",
53
55
  children: "Take theirs"
54
56
  }
55
57
  ),
56
- /* @__PURE__ */ o(
58
+ /* @__PURE__ */ s(
57
59
  "button",
58
60
  {
59
61
  type: "button",
@@ -65,300 +67,465 @@ function oe({
65
67
  )
66
68
  ]
67
69
  }
68
- ) : /* @__PURE__ */ x(
70
+ ) : /* @__PURE__ */ y(
69
71
  "div",
70
72
  {
71
73
  "data-remote-change-ghost": !0,
72
74
  className: "nvr-expand-in flex items-start gap-1.5 rounded-md border border-sky-200 bg-sky-50 px-2 py-1 text-[11.5px] text-sky-900 dark:border-sky-500/30 dark:bg-sky-500/10 dark:text-sky-200",
73
75
  children: [
74
- /* @__PURE__ */ o("span", { className: "mt-0.5 h-1.5 w-1.5 shrink-0 animate-pulse rounded-full bg-sky-500" }),
75
- /* @__PURE__ */ x("span", { className: "min-w-0 flex-1", children: [
76
+ /* @__PURE__ */ s("span", { className: "mt-0.5 h-1.5 w-1.5 shrink-0 animate-pulse rounded-full bg-sky-500" }),
77
+ /* @__PURE__ */ y("span", { className: "min-w-0 flex-1", children: [
76
78
  "was",
77
79
  " ",
78
- /* @__PURE__ */ o("span", { className: "font-medium line-through decoration-sky-400/70", children: j(e.was) }),
79
- /* @__PURE__ */ x("span", { className: "text-sky-700/80 dark:text-sky-300/80", children: [
80
+ /* @__PURE__ */ s("span", { className: "font-medium line-through decoration-sky-400/70", children: V(t.was) }),
81
+ /* @__PURE__ */ y("span", { className: "text-sky-700/80 dark:text-sky-300/80", children: [
80
82
  " ",
81
83
  "· ",
82
- e.by ?? "someone",
84
+ t.by ?? "someone",
83
85
  ", ",
84
- T(e.at)
86
+ I(t.at)
85
87
  ] })
86
88
  ] }),
87
- /* @__PURE__ */ o(
89
+ /* @__PURE__ */ s(
88
90
  "button",
89
91
  {
90
92
  type: "button",
91
93
  onClick: n,
92
94
  "aria-label": "Dismiss",
93
95
  className: "shrink-0 rounded p-0.5 text-sky-500 hover:bg-sky-100 hover:text-sky-800 dark:hover:bg-sky-500/20",
94
- children: /* @__PURE__ */ o(I, { className: "h-3 w-3" })
96
+ children: /* @__PURE__ */ s(X, { className: "h-3 w-3" })
95
97
  }
96
98
  )
97
99
  ]
98
100
  }
99
101
  );
100
102
  }
101
- function ae({ reason: e, onHover: n }) {
102
- return /* @__PURE__ */ o(
103
+ function Ee({ reason: t, onHover: n }) {
104
+ return /* @__PURE__ */ s(
103
105
  "span",
104
106
  {
105
107
  onPointerEnter: n,
106
108
  className: "inline-flex cursor-help items-center gap-1 text-amber-500 dark:text-amber-400",
107
- "data-tip": e,
109
+ "data-tip": t,
108
110
  "data-lock-reason": !0,
109
- "aria-label": e,
111
+ "aria-label": t,
110
112
  role: "img",
111
- children: /* @__PURE__ */ o(H, { className: "h-3 w-3" })
113
+ children: /* @__PURE__ */ s(oe, { className: "h-3 w-3" })
112
114
  }
113
115
  );
114
116
  }
115
- function W(e, n) {
116
- if (!e || !(n != null && n.contains(e))) return null;
117
- const t = e.closest(
117
+ function P(t, n) {
118
+ var a, c, d;
119
+ const e = t;
120
+ return ((c = (a = e == null ? void 0 : e.response) == null ? void 0 : a.data) == null ? void 0 : c.error) ?? ((d = e == null ? void 0 : e.data) == null ? void 0 : d.error) ?? (e == null ? void 0 : e.error) ?? (e == null ? void 0 : e.message) ?? n;
121
+ }
122
+ function fe(t, n) {
123
+ if (!t || !(n != null && n.contains(t))) return null;
124
+ const e = t.closest(
118
125
  "input, textarea, select"
119
126
  );
120
- if (t && n.contains(t) && !(t instanceof HTMLInputElement && t.type === "checkbox"))
121
- return t.value;
122
- const s = e.closest("td, th");
123
- return s && n.contains(s) ? s.innerText.replace(/\s+/g, " ").trim() : null;
127
+ if (e && n.contains(e) && !(e instanceof HTMLInputElement && e.type === "checkbox"))
128
+ return e.value;
129
+ const a = t.closest("td, th");
130
+ return a && n.contains(a) ? a.innerText.replace(/\s+/g, " ").trim() : null;
124
131
  }
125
- function se({
126
- wrapperRef: e,
132
+ function Te({
133
+ wrapperRef: t,
127
134
  field: n,
128
- value: t,
129
- relations: s,
130
- collection: m,
131
- itemId: c,
132
- openRelated: l
135
+ value: e,
136
+ relations: a,
137
+ collection: c,
138
+ itemId: d,
139
+ openRelated: g,
140
+ editable: D = !1
133
141
  }) {
134
- var M, D;
135
- const [y, w] = C(null), h = g((a, u) => w({ x: a, y: u }), []), b = g(() => w(null), []), r = v(null), f = g(
136
- (a) => {
137
- var L;
138
- const u = a.target;
139
- (L = window.getSelection()) != null && L.toString() && u.closest("input, textarea, [contenteditable]") || (a.preventDefault(), r.current = u, h(a.clientX, a.clientY));
142
+ var B, H;
143
+ const [p, S] = M(null), l = ue(), [k, E] = M(!1), [f, O] = M(null), [U, A] = M(!1), v = F((r, u) => S({ x: r, y: u }), []), w = j(null), T = F(() => {
144
+ S(null), E(!1), O(null), A(!1);
145
+ const r = w.current;
146
+ w.current = null, r && window.setTimeout(() => {
147
+ r.isConnected && (!document.activeElement || document.activeElement === document.body) && r.focus();
148
+ }, 0);
149
+ }, []), o = p !== null, b = F(
150
+ (r) => {
151
+ if (o) {
152
+ T();
153
+ return;
154
+ }
155
+ const u = r.getBoundingClientRect();
156
+ N.current = null, w.current = r, v(u.left, u.bottom + 4);
140
157
  },
141
- [h]
142
- ), N = g(
143
- (a) => {
158
+ [o, T, v]
159
+ ), N = j(null), m = F(
160
+ (r) => {
161
+ var K;
162
+ const u = r.target;
163
+ (K = window.getSelection()) != null && K.toString() && u.closest("input, textarea, [contenteditable]") || (r.preventDefault(), N.current = u, v(r.clientX, r.clientY));
164
+ },
165
+ [v]
166
+ ), x = F(
167
+ (r) => {
144
168
  var u;
145
- if (a.key === "ContextMenu" || a.shiftKey && a.key === "F10") {
146
- a.preventDefault();
147
- const p = (u = e.current) == null ? void 0 : u.getBoundingClientRect();
148
- r.current = document.activeElement, h(p ? p.left + 16 : 16, p ? p.top + 28 : 16);
169
+ if (r.key === "ContextMenu" || r.shiftKey && r.key === "F10") {
170
+ r.preventDefault();
171
+ const L = (u = t.current) == null ? void 0 : u.getBoundingClientRect();
172
+ N.current = document.activeElement, v(L ? L.left + 16 : 16, L ? L.top + 28 : 16);
149
173
  }
150
174
  },
151
- [h, e]
152
- ), i = s.find(
153
- (a) => a.many_collection === m && a.many_field === n.field && a.one_collection
154
- ), d = c && c !== "new", S = (a) => {
155
- var p;
156
- const u = (p = e.current) == null ? void 0 : p.querySelector(a);
175
+ [v, t]
176
+ ), i = a.find(
177
+ (r) => r.many_collection === c && r.many_field === n.field && r.one_collection
178
+ ), C = d && d !== "new", R = (r) => {
179
+ var L;
180
+ const u = (L = t.current) == null ? void 0 : L.querySelector(r);
157
181
  return u ? (u.click(), !0) : !1;
158
- }, _ = [
182
+ }, $ = [
159
183
  {
160
184
  key: "copy",
161
185
  label: "Copy value",
162
- icon: /* @__PURE__ */ o(F, { className: "h-3.5 w-3.5" }),
186
+ icon: /* @__PURE__ */ s(Y, { className: "h-3.5 w-3.5" }),
163
187
  run: () => {
164
- const u = W(r.current, e.current) ?? (t == null ? "" : typeof t == "object" ? JSON.stringify(t) : String(t));
188
+ const u = fe(N.current, t.current) ?? (e == null ? "" : typeof e == "object" ? JSON.stringify(e) : String(e));
165
189
  if (!u) {
166
- k.info("Nothing to copy");
190
+ h.info("Nothing to copy");
167
191
  return;
168
192
  }
169
193
  navigator.clipboard.writeText(u).then(
170
- () => k.success("Value copied"),
171
- () => k.error("Could not copy")
194
+ () => h.success("Value copied"),
195
+ () => h.error("Could not copy")
172
196
  );
173
197
  }
174
198
  },
175
199
  {
176
200
  key: "copy-key",
177
201
  label: "Copy field key",
178
- icon: /* @__PURE__ */ o(F, { className: "h-3.5 w-3.5" }),
202
+ icon: /* @__PURE__ */ s(Y, { className: "h-3.5 w-3.5" }),
179
203
  hint: n.field,
180
204
  run: () => {
181
205
  navigator.clipboard.writeText(n.field).then(
182
- () => k.success(`Copied “${n.field}”`),
183
- () => k.error("Could not copy")
206
+ () => h.success(`Copied “${n.field}”`),
207
+ () => h.error("Could not copy")
184
208
  );
185
209
  }
186
210
  },
187
211
  {
188
212
  key: "history",
189
213
  label: "Value history",
190
- icon: /* @__PURE__ */ o(O, { className: "h-3.5 w-3.5" }),
191
- disabled: !d,
214
+ icon: /* @__PURE__ */ s(Q, { className: "h-3.5 w-3.5" }),
215
+ disabled: !C,
192
216
  run: () => {
193
- S("[data-field-history]") || k.info("No history for this field yet");
217
+ R("[data-field-history]") || h.info("No history for this field yet");
194
218
  }
195
219
  },
196
220
  {
197
221
  key: "watch",
198
222
  label: "Watch this field",
199
- icon: /* @__PURE__ */ o(K, { className: "h-3.5 w-3.5" }),
200
- disabled: !d,
223
+ icon: /* @__PURE__ */ s(Z, { className: "h-3.5 w-3.5" }),
224
+ disabled: !C,
201
225
  run: () => {
202
- S("[data-field-watch]") || k.info("Field watching is off — an admin can enable it in Settings → Content");
226
+ R("[data-field-watch]") || h.info("Field watching is off — an admin can enable it in Settings → Content");
203
227
  }
204
228
  },
205
229
  {
206
230
  key: "explain",
207
231
  label: "Explain value",
208
- icon: /* @__PURE__ */ o(P, { className: "h-3.5 w-3.5" }),
209
- disabled: !d || !((M = e.current) != null && M.querySelector("[data-field-lineage]")),
232
+ icon: /* @__PURE__ */ s(ee, { className: "h-3.5 w-3.5" }),
233
+ disabled: !C || !((B = t.current) != null && B.querySelector("[data-field-lineage]")),
210
234
  run: () => {
211
- S("[data-field-lineage]");
235
+ R("[data-field-lineage]");
212
236
  }
213
237
  },
214
- ...i && l ? [
238
+ ...i && g ? [
215
239
  {
216
240
  key: "open",
217
- label: `Open related ${(D = i.one_collection) == null ? void 0 : D.replace(/_/g, " ")}`,
218
- icon: /* @__PURE__ */ o(B, { className: "h-3.5 w-3.5" }),
219
- disabled: t == null || t === "",
241
+ label: `Open related ${(H = i.one_collection) == null ? void 0 : H.replace(/_/g, " ")}`,
242
+ icon: /* @__PURE__ */ s(te, { className: "h-3.5 w-3.5" }),
243
+ disabled: e == null || e === "",
244
+ run: () => {
245
+ const r = typeof e == "object" && e && "id" in e ? String(e.id) : String(e);
246
+ g(String(i.one_collection), r);
247
+ }
248
+ }
249
+ ] : [],
250
+ ...l != null && l.allowed(n.field) && D && l.hasValue(n.field) ? [
251
+ {
252
+ key: "save-default",
253
+ label: "Save as my default",
254
+ icon: /* @__PURE__ */ s(ne, { className: "h-3.5 w-3.5" }),
255
+ stay: !0,
220
256
  run: () => {
221
- const a = typeof t == "object" && t && "id" in t ? String(t.id) : String(t);
222
- l(String(i.one_collection), a);
257
+ O(null), E(!0), l.describeSave(n.field).then(
258
+ (r) => O(r),
259
+ () => O([])
260
+ );
261
+ }
262
+ }
263
+ ] : [],
264
+ ...l != null && l.allowed(n.field) && l.defaultFor(n.field) ? [
265
+ {
266
+ key: "clear-default",
267
+ label: "Clear my default",
268
+ icon: /* @__PURE__ */ s(re, { className: "h-3.5 w-3.5" }),
269
+ // Runs at once (a menu pick is deliberate); the toast's Undo saves it back.
270
+ run: () => {
271
+ l.clear(n.field).then(
272
+ (r) => h.success(
273
+ "Your default was cleared",
274
+ r ? {
275
+ action: {
276
+ label: "Undo",
277
+ onClick: () => {
278
+ l.restore(r).then(
279
+ () => h.success("Your default is back"),
280
+ (u) => h.error(P(u, "Could not put your default back"))
281
+ );
282
+ }
283
+ }
284
+ } : void 0
285
+ ),
286
+ (r) => h.error(P(r, "Could not clear your default"))
287
+ );
223
288
  }
224
289
  }
225
290
  ] : []
226
- ];
227
- return { onContextMenu: f, onKeyDown: N, menu: y ? /* @__PURE__ */ o(X, { at: y, items: _, onClose: b }) : null };
291
+ ], G = k && l ? {
292
+ text: f != null && f.length ? `Save ${f.join(" · ")} as your ${f.length > 1 ? "defaults" : "default"}?` : "Save as your default?",
293
+ ready: f !== null && !U,
294
+ saving: U,
295
+ onCancel: T,
296
+ onSave: () => {
297
+ A(!0), l.save(n.field).then(
298
+ () => {
299
+ h.success(
300
+ f != null && f.length ? `Saved ${f.join(" · ")} as your default${f.length > 1 ? "s" : ""}` : "Your default was saved"
301
+ ), T();
302
+ },
303
+ (r) => {
304
+ h.error(P(r, "Could not save your default")), T();
305
+ }
306
+ );
307
+ }
308
+ } : null, J = p ? /* @__PURE__ */ s(
309
+ be,
310
+ {
311
+ at: p,
312
+ items: $,
313
+ onClose: T,
314
+ confirm: G,
315
+ anchor: t.current
316
+ }
317
+ ) : null, W = $.some((r) => r.key === "save-default" || r.key === "clear-default");
318
+ return { onContextMenu: m, onKeyDown: x, menu: J, openFrom: b, isOpen: o, hasFieldItems: W };
228
319
  }
229
- function X({
230
- at: e,
320
+ function be({
321
+ at: t,
231
322
  items: n,
232
- onClose: t
323
+ onClose: e,
324
+ confirm: a,
325
+ anchor: c
233
326
  }) {
234
- const s = v(null), [m, c] = C(0), l = n.filter((r) => !r.disabled);
235
- E(() => {
327
+ const d = j(null), [g, D] = M(0), p = n.filter((o) => !o.disabled);
328
+ _(() => {
236
329
  var N;
237
- const r = (i) => {
238
- var d;
239
- (d = s.current) != null && d.contains(i.target) || t();
240
- }, f = (i) => {
241
- if (i.key === "Escape")
242
- i.preventDefault(), t();
243
- else if (i.key === "ArrowDown")
244
- i.preventDefault(), c((d) => (d + 1) % Math.max(1, l.length));
245
- else if (i.key === "ArrowUp")
246
- i.preventDefault(), c((d) => (d - 1 + Math.max(1, l.length)) % Math.max(1, l.length));
247
- else if (i.key === "Enter" || i.key === " ") {
248
- i.preventDefault();
249
- const d = l[m];
250
- d && (d.run(), t());
330
+ const o = (m) => {
331
+ var i, C;
332
+ const x = m.target;
333
+ (i = d.current) != null && i.contains(x) || (C = x == null ? void 0 : x.closest) != null && C.call(x, "[data-field-menu-trigger]") && (c != null && c.contains(x)) || e();
334
+ }, b = (m) => {
335
+ var x;
336
+ if (m.key === "Escape")
337
+ m.preventDefault(), e();
338
+ else if (a) {
339
+ if (m.key === "Tab") {
340
+ m.preventDefault();
341
+ const i = [l.current, S.current].filter(
342
+ ($) => !!$ && !$.disabled
343
+ );
344
+ if (i.length === 0) return;
345
+ const C = i.indexOf(document.activeElement), R = m.shiftKey ? C - 1 : C + 1;
346
+ (x = i[(R % i.length + i.length) % i.length]) == null || x.focus();
347
+ }
348
+ return;
349
+ } else if (m.key === "ArrowDown")
350
+ m.preventDefault(), D((i) => (i + 1) % Math.max(1, p.length));
351
+ else if (m.key === "ArrowUp")
352
+ m.preventDefault(), D((i) => (i - 1 + Math.max(1, p.length)) % Math.max(1, p.length));
353
+ else if (m.key === "Enter" || m.key === " ") {
354
+ m.preventDefault();
355
+ const i = p[g];
356
+ i && (i.run(), i.stay || e());
251
357
  }
252
358
  };
253
- return window.addEventListener("mousedown", r, !0), window.addEventListener("keydown", f, !0), (N = s.current) == null || N.focus(), () => {
254
- window.removeEventListener("mousedown", r, !0), window.removeEventListener("keydown", f, !0);
359
+ return window.addEventListener("mousedown", o, !0), window.addEventListener("keydown", b, !0), (N = d.current) == null || N.focus(), () => {
360
+ window.removeEventListener("mousedown", o, !0), window.removeEventListener("keydown", b, !0);
361
+ };
362
+ }, [e, p, g, a, c]);
363
+ const S = j(null), l = j(null), k = !!a, E = !!(a != null && a.ready);
364
+ _(() => {
365
+ var o;
366
+ k && ((o = l.current) == null || o.focus());
367
+ }, [k]), _(() => {
368
+ var b;
369
+ if (!E) return;
370
+ const o = document.activeElement;
371
+ (o === l.current || o === d.current || o === document.body) && ((b = S.current) == null || b.focus());
372
+ }, [E]);
373
+ const f = 240, [O, U] = M(() => a ? 96 : n.length * 30 + 12);
374
+ le(() => {
375
+ var b;
376
+ const o = (b = d.current) == null ? void 0 : b.offsetHeight;
377
+ o && o !== O && U(o);
378
+ });
379
+ const A = Math.max(8, Math.min(t.x, window.innerWidth - f - 8)), v = Math.max(8, Math.min(t.y, window.innerHeight - O - 8)), w = (c == null ? void 0 : c.closest('[role="dialog"]')) ?? document.body, T = w === document.body ? { position: "fixed", left: A, top: v } : (() => {
380
+ const o = w.getBoundingClientRect();
381
+ return {
382
+ position: "absolute",
383
+ left: A - o.left + w.scrollLeft - w.clientLeft,
384
+ top: v - o.top + w.scrollTop - w.clientTop
255
385
  };
256
- }, [t, l, m]);
257
- const y = 240, w = n.length * 30 + 12, h = Math.min(e.x, window.innerWidth - y - 8), b = Math.min(e.y, window.innerHeight - w - 8);
258
- return J(
259
- /* @__PURE__ */ o(
386
+ })();
387
+ return ie(
388
+ /* @__PURE__ */ y(
260
389
  "div",
261
390
  {
262
- ref: s,
263
- role: "menu",
391
+ ref: d,
392
+ ...a ? { role: "dialog", "aria-modal": !1, "aria-label": "Save as my default" } : { role: "menu", "aria-label": "Field options" },
264
393
  tabIndex: -1,
265
394
  "data-field-context-menu": !0,
266
- style: { position: "fixed", left: h, top: b, width: y, zIndex: 110 },
395
+ style: { ...T, width: f, zIndex: 110 },
267
396
  className: "rounded-lg border border-slate-200 bg-white p-1 shadow-xl outline-none dark:border-border dark:bg-card",
268
- children: n.map((r) => {
269
- const f = l.indexOf(r), N = f === m && !r.disabled;
270
- return /* @__PURE__ */ x(
271
- "button",
272
- {
273
- type: "button",
274
- role: "menuitem",
275
- disabled: r.disabled,
276
- onMouseEnter: () => f >= 0 && c(f),
277
- onClick: () => {
278
- r.run(), t();
279
- },
280
- className: A(
281
- "flex h-[30px] w-full items-center gap-2 rounded-sm px-2 text-left text-[12.5px] font-medium text-slate-700 dark:text-slate-200",
282
- N && "bg-accent text-accent-foreground",
283
- r.disabled && "cursor-not-allowed opacity-40"
397
+ children: [
398
+ a ? /* @__PURE__ */ y("div", { "data-personal-default-confirm": !0, className: "space-y-2 px-2 py-1.5", children: [
399
+ /* @__PURE__ */ s(
400
+ "p",
401
+ {
402
+ "aria-live": "polite",
403
+ className: "text-[12.5px] leading-snug text-slate-700 dark:text-slate-200",
404
+ children: a.text
405
+ }
406
+ ),
407
+ /* @__PURE__ */ y("div", { className: "flex justify-end gap-1.5", children: [
408
+ /* @__PURE__ */ y(
409
+ "button",
410
+ {
411
+ ref: l,
412
+ type: "button",
413
+ onClick: a.onCancel,
414
+ className: de,
415
+ children: [
416
+ /* @__PURE__ */ s(X, { className: "h-3 w-3", "aria-hidden": !0 }),
417
+ " Cancel"
418
+ ]
419
+ }
284
420
  ),
285
- children: [
286
- /* @__PURE__ */ o("span", { className: "shrink-0 text-slate-500", children: r.icon }),
287
- /* @__PURE__ */ o("span", { className: "min-w-0 flex-1 truncate", children: r.label }),
288
- r.hint && /* @__PURE__ */ o("span", { className: "max-w-[90px] truncate font-mono text-[10.5px] text-slate-400", children: r.hint })
289
- ]
290
- },
291
- r.key
292
- );
293
- })
421
+ /* @__PURE__ */ s(
422
+ "button",
423
+ {
424
+ ref: S,
425
+ type: "button",
426
+ disabled: !a.ready,
427
+ onClick: a.onSave,
428
+ className: "h-7 rounded-md bg-nvr-cyan px-2.5 text-[11.5px] font-semibold text-white hover:bg-nvr-cyan/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-nvr-cyan/40 focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50",
429
+ children: a.saving ? "Saving…" : "Save"
430
+ }
431
+ )
432
+ ] })
433
+ ] }) : null,
434
+ a ? null : n.map((o) => {
435
+ const b = p.indexOf(o), N = b === g && !o.disabled;
436
+ return /* @__PURE__ */ y(
437
+ "button",
438
+ {
439
+ type: "button",
440
+ role: "menuitem",
441
+ disabled: o.disabled,
442
+ onMouseEnter: () => b >= 0 && D(b),
443
+ onClick: () => {
444
+ o.run(), o.stay || e();
445
+ },
446
+ className: q(
447
+ "flex h-[30px] w-full items-center gap-2 rounded-sm px-2 text-left text-[12.5px] font-medium text-slate-700 dark:text-slate-200",
448
+ N && "bg-accent text-accent-foreground",
449
+ o.disabled && "cursor-not-allowed opacity-40"
450
+ ),
451
+ children: [
452
+ /* @__PURE__ */ s("span", { className: "shrink-0 text-slate-500", children: o.icon }),
453
+ /* @__PURE__ */ s("span", { className: "min-w-0 flex-1 truncate", children: o.label }),
454
+ o.hint && /* @__PURE__ */ s("span", { className: "max-w-[90px] truncate font-mono text-[10.5px] text-slate-400", children: o.hint })
455
+ ]
456
+ },
457
+ o.key
458
+ );
459
+ })
460
+ ]
294
461
  }
295
462
  ),
296
- document.body
463
+ w
297
464
  );
298
465
  }
299
- function ie(e) {
300
- const [n, t] = C(null), [s, m] = C(!1), c = v(!1), l = v(null);
301
- E(() => {
302
- const b = e.current;
303
- if (!b) return;
304
- const r = () => t(b.getAttribute("data-remote-editor"));
305
- r();
306
- const f = new MutationObserver(r);
307
- return f.observe(b, { attributes: !0, attributeFilter: ["data-remote-editor"] }), () => f.disconnect();
308
- }, [e]), E(() => {
309
- n || (m(!1), c.current = !1);
466
+ function Le(t) {
467
+ const [n, e] = M(null), [a, c] = M(!1), d = j(!1), g = j(null);
468
+ _(() => {
469
+ const l = t.current;
470
+ if (!l) return;
471
+ const k = () => e(l.getAttribute("data-remote-editor"));
472
+ k();
473
+ const E = new MutationObserver(k);
474
+ return E.observe(l, { attributes: !0, attributeFilter: ["data-remote-editor"] }), () => E.disconnect();
475
+ }, [t]), _(() => {
476
+ n || (c(!1), d.current = !1);
310
477
  }, [n]);
311
- const y = g(
312
- (b) => {
313
- if (!n || c.current) return;
314
- const r = b.target;
315
- r.matches('input, textarea, select, [contenteditable="true"], [role="combobox"]') && (l.current = r, setTimeout(() => {
316
- c.current || r.blur();
317
- }, 0), m(!0));
478
+ const D = F(
479
+ (l) => {
480
+ if (!n || d.current) return;
481
+ const k = l.target;
482
+ k.matches('input, textarea, select, [contenteditable="true"], [role="combobox"]') && (g.current = k, setTimeout(() => {
483
+ d.current || k.blur();
484
+ }, 0), c(!0));
318
485
  },
319
486
  [n]
320
- ), w = g(() => {
321
- c.current = !0, m(!1);
322
- const b = l.current;
323
- l.current = null, b && setTimeout(() => b.focus(), 0);
324
- }, []), h = g(() => {
325
- m(!1), l.current = null;
487
+ ), p = F(() => {
488
+ d.current = !0, c(!1);
489
+ const l = g.current;
490
+ g.current = null, l && setTimeout(() => l.focus(), 0);
491
+ }, []), S = F(() => {
492
+ c(!1), g.current = null;
326
493
  }, []);
327
- return { editor: n, confirming: s, onFocusCapture: y, confirm: w, cancel: h };
494
+ return { editor: n, confirming: a, onFocusCapture: D, confirm: p, cancel: S };
328
495
  }
329
- function le({
330
- editor: e,
496
+ function Fe({
497
+ editor: t,
331
498
  confirming: n,
332
- onConfirm: t,
333
- onCancel: s
499
+ onConfirm: e,
500
+ onCancel: a
334
501
  }) {
335
- const m = e.split(/\s+/).map((c) => c[0]).filter(Boolean).slice(0, 2).join("").toUpperCase();
336
- return /* @__PURE__ */ x(
502
+ const c = t.split(/\s+/).map((d) => d[0]).filter(Boolean).slice(0, 2).join("").toUpperCase();
503
+ return /* @__PURE__ */ y(
337
504
  "div",
338
505
  {
339
506
  "data-presence-soft-lock": !0,
340
- className: A(
507
+ className: q(
341
508
  "flex flex-wrap items-center gap-2 rounded-md px-2 py-1 text-[11.5px]",
342
509
  n ? "border border-amber-300 bg-amber-50 text-amber-900 dark:border-amber-500/40 dark:bg-amber-500/10 dark:text-amber-100" : "text-amber-700 dark:text-amber-300"
343
510
  ),
344
511
  children: [
345
- /* @__PURE__ */ o("span", { className: "inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-amber-200 text-[9px] font-semibold text-amber-900 dark:bg-amber-500/40 dark:text-amber-50", children: m || "?" }),
346
- /* @__PURE__ */ o("span", { className: "min-w-0 flex-1", children: n ? `${e} is typing here — edit anyway?` : `${e} is editing this field` }),
347
- n && /* @__PURE__ */ x($, { children: [
348
- /* @__PURE__ */ o(
512
+ /* @__PURE__ */ s("span", { className: "inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-amber-200 text-[9px] font-semibold text-amber-900 dark:bg-amber-500/40 dark:text-amber-50", children: c || "?" }),
513
+ /* @__PURE__ */ s("span", { className: "min-w-0 flex-1", children: n ? `${t} is typing here — edit anyway?` : `${t} is editing this field` }),
514
+ n && /* @__PURE__ */ y(z, { children: [
515
+ /* @__PURE__ */ s(
349
516
  "button",
350
517
  {
351
518
  type: "button",
352
- onClick: t,
519
+ onClick: e,
353
520
  className: "rounded-md bg-amber-600 px-2 py-0.5 text-[11px] font-semibold text-white hover:bg-amber-700",
354
521
  children: "Edit anyway"
355
522
  }
356
523
  ),
357
- /* @__PURE__ */ o(
524
+ /* @__PURE__ */ s(
358
525
  "button",
359
526
  {
360
527
  type: "button",
361
- onClick: s,
528
+ onClick: a,
362
529
  className: "rounded-md border border-amber-300 px-2 py-0.5 text-[11px] font-medium hover:bg-amber-100 dark:border-amber-500/40 dark:hover:bg-amber-500/15",
363
530
  children: "Leave it"
364
531
  }
@@ -369,12 +536,12 @@ function le({
369
536
  );
370
537
  }
371
538
  export {
372
- V as FieldAffordancesContext,
373
- ae as LockReasonBadge,
374
- le as PresenceSoftLockStrip,
375
- oe as RemoteChangeGhost,
376
- j as ghostText,
377
- re as useFieldAffordances,
378
- se as useFieldContextMenu,
379
- ie as usePresenceSoftLock
539
+ me as FieldAffordancesContext,
540
+ Ee as LockReasonBadge,
541
+ Fe as PresenceSoftLockStrip,
542
+ Se as RemoteChangeGhost,
543
+ V as ghostText,
544
+ Ce as useFieldAffordances,
545
+ Te as useFieldContextMenu,
546
+ Le as usePresenceSoftLock
380
547
  };