@wildwinter/app-shell 0.27.0 → 0.29.0

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.cjs CHANGED
@@ -1,5 +1,154 @@
1
1
  'use strict';
2
2
 
3
+ // src/tooltip.ts
4
+ var B_OPEN = "";
5
+ var B_CLOSE = "";
6
+ function tipBold(s) {
7
+ return `${B_OPEN}${s}${B_CLOSE}`;
8
+ }
9
+ function renderTip(host, text) {
10
+ host.textContent = "";
11
+ const re = /\u0002([^\u0003]*)\u0003/g;
12
+ let last = 0;
13
+ let m;
14
+ while ((m = re.exec(text)) !== null) {
15
+ if (m.index > last) host.append(text.slice(last, m.index));
16
+ const strong = document.createElement("strong");
17
+ strong.textContent = m[1] ?? "";
18
+ host.append(strong);
19
+ last = m.index + m[0].length;
20
+ }
21
+ if (last < text.length) host.append(text.slice(last));
22
+ }
23
+ var inited = false;
24
+ var checked = false;
25
+ var suppressed;
26
+ var SHOW_DELAY = 350;
27
+ var EDGE = 6;
28
+ var GAP = 7;
29
+ var tip = null;
30
+ var timer = 0;
31
+ var active = null;
32
+ var activeKey = null;
33
+ function ensureEl() {
34
+ if (tip) return tip;
35
+ const el2 = document.createElement("div");
36
+ el2.className = "tooltip";
37
+ el2.setAttribute("role", "tooltip");
38
+ el2.hidden = true;
39
+ document.body.appendChild(el2);
40
+ tip = el2;
41
+ return el2;
42
+ }
43
+ var rectOf = (anchor) => anchor instanceof HTMLElement ? anchor.getBoundingClientRect() : anchor;
44
+ function place(anchor) {
45
+ const t = ensureEl();
46
+ const r = rectOf(anchor);
47
+ const tr = t.getBoundingClientRect();
48
+ let top = r.top - tr.height - GAP;
49
+ if (top < EDGE) top = r.top + r.height + GAP;
50
+ let left = r.left + r.width / 2 - tr.width / 2;
51
+ left = Math.max(EDGE, Math.min(left, window.innerWidth - tr.width - EDGE));
52
+ t.style.left = `${Math.round(left)}px`;
53
+ t.style.top = `${Math.round(top)}px`;
54
+ }
55
+ function show(anchor) {
56
+ const text = anchor.dataset["tip"];
57
+ if (!text) return;
58
+ paint(anchor, text);
59
+ }
60
+ function paint(anchor, text) {
61
+ if (suppressed?.()) return;
62
+ const t = ensureEl();
63
+ const host = (anchor instanceof HTMLElement ? anchor.closest("dialog[open]") : null) ?? document.body;
64
+ if (t.parentElement !== host) host.appendChild(t);
65
+ active = anchor instanceof HTMLElement ? anchor : null;
66
+ renderTip(t, text);
67
+ t.hidden = false;
68
+ t.classList.remove("show");
69
+ void t.offsetWidth;
70
+ place(anchor);
71
+ t.classList.add("show");
72
+ }
73
+ function hide() {
74
+ if (timer) {
75
+ window.clearTimeout(timer);
76
+ timer = 0;
77
+ }
78
+ active = null;
79
+ activeKey = null;
80
+ if (tip) {
81
+ tip.hidden = true;
82
+ tip.classList.remove("show");
83
+ }
84
+ }
85
+ function schedule(anchor) {
86
+ if (anchor === active) return;
87
+ if (timer) window.clearTimeout(timer);
88
+ timer = window.setTimeout(() => show(anchor), SHOW_DELAY);
89
+ }
90
+ function tipAt(key, rect, text) {
91
+ if (key === activeKey) {
92
+ if (tip && !tip.hidden) {
93
+ renderTip(tip, text);
94
+ place(rect);
95
+ }
96
+ return;
97
+ }
98
+ hide();
99
+ activeKey = key;
100
+ timer = window.setTimeout(() => {
101
+ timer = 0;
102
+ paint(rect, text);
103
+ }, SHOW_DELAY);
104
+ }
105
+ function hideTip() {
106
+ hide();
107
+ }
108
+ function ensureTooltipHost() {
109
+ initTooltips();
110
+ }
111
+ function checkTooltipHost() {
112
+ if (checked) return;
113
+ checked = true;
114
+ setTimeout(() => {
115
+ if (inited || typeof document === "undefined") return;
116
+ if (document.querySelector("[data-tip]")) {
117
+ console.warn("app-shell: something set `data-tip` but no tooltip host is mounted, so those rollovers will not draw. Call initTooltips() once per window.");
118
+ }
119
+ }, 0);
120
+ }
121
+ var tipAncestor = (n) => n instanceof Element ? n.closest("[data-tip]") : null;
122
+ function initTooltips(opts = {}) {
123
+ if (opts.suppressed !== void 0) suppressed = opts.suppressed;
124
+ if (inited) return;
125
+ inited = true;
126
+ document.addEventListener("pointerover", (e) => {
127
+ const el2 = tipAncestor(e.target);
128
+ if (el2) schedule(el2);
129
+ });
130
+ document.addEventListener("pointerout", (e) => {
131
+ const el2 = tipAncestor(e.target);
132
+ if (!el2) return;
133
+ const to = e.relatedTarget;
134
+ if (to instanceof Node && el2.contains(to)) return;
135
+ hide();
136
+ });
137
+ document.addEventListener("focusin", (e) => {
138
+ const el2 = tipAncestor(e.target);
139
+ if (el2) schedule(el2);
140
+ });
141
+ document.addEventListener("focusout", () => hide());
142
+ document.addEventListener("pointerdown", () => hide(), true);
143
+ document.addEventListener("scroll", () => {
144
+ if (active) hide();
145
+ }, true);
146
+ document.addEventListener("keydown", (e) => {
147
+ if (e.key === "Escape") hide();
148
+ });
149
+ window.addEventListener("blur", () => hide());
150
+ }
151
+
3
152
  // src/dom.ts
4
153
  function el(tag, classNameOrProps, ...children) {
5
154
  const node = document.createElement(tag);
@@ -10,12 +159,14 @@ function el(tag, classNameOrProps, ...children) {
10
159
  if (props.tip !== void 0) {
11
160
  node.dataset["tip"] = props.tip;
12
161
  node.setAttribute("aria-label", props.tip);
162
+ ensureTooltipHost();
13
163
  }
14
164
  if (props.onClick) node.addEventListener("click", props.onClick);
15
165
  for (const child of children) {
16
166
  if (child === null || child === void 0) continue;
17
167
  node.append(child);
18
168
  }
169
+ checkTooltipHost();
19
170
  return node;
20
171
  }
21
172
  function iconBtn(glyph, title, onClick, disabled = false, danger = false) {
@@ -721,6 +872,25 @@ function isValidGameId(gameId) {
721
872
  return /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(gameId);
722
873
  }
723
874
 
875
+ // src/property-names.ts
876
+ var RESERVED_PROPERTY_NAMES = ["true", "false", "and", "or", "not"];
877
+ var PROPERTY_NAME_HINT = "Lower case letters, digits and underscores. Cannot start with a digit. No hyphens or spaces.";
878
+ function propertyNameify(text) {
879
+ const trimmed = text.trim();
880
+ const deliberateLeading = trimmed.startsWith("_");
881
+ let out = trimmed.toLowerCase().replace(/['’]/g, "").replace(/[^a-z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
882
+ if (out === "") return "";
883
+ if (deliberateLeading || /^[0-9]/.test(out)) out = `_${out}`;
884
+ if (RESERVED_PROPERTY_NAMES.includes(out)) out = `${out}_`;
885
+ return out;
886
+ }
887
+ function isValidPropertyName(name) {
888
+ return /^[a-z_][a-z0-9_]*$/.test(name) && !RESERVED_PROPERTY_NAMES.includes(name);
889
+ }
890
+ function isCaseOnlyPropertyName(name) {
891
+ return !isValidPropertyName(name) && isValidPropertyName(name.toLowerCase());
892
+ }
893
+
724
894
  // src/id-editor.ts
725
895
  function openGameIdEditor(opts) {
726
896
  const slugify = opts.slugify ?? gameIdify;
@@ -1198,141 +1368,6 @@ function mountJobProgress(host, opts) {
1198
1368
  // src/job.ts
1199
1369
  var JOB_PROGRESS = "job:progress";
1200
1370
 
1201
- // src/tooltip.ts
1202
- var B_OPEN = "";
1203
- var B_CLOSE = "";
1204
- function tipBold(s) {
1205
- return `${B_OPEN}${s}${B_CLOSE}`;
1206
- }
1207
- function renderTip(host, text) {
1208
- host.textContent = "";
1209
- const re = /\u0002([^\u0003]*)\u0003/g;
1210
- let last = 0;
1211
- let m;
1212
- while ((m = re.exec(text)) !== null) {
1213
- if (m.index > last) host.append(text.slice(last, m.index));
1214
- const strong = document.createElement("strong");
1215
- strong.textContent = m[1] ?? "";
1216
- host.append(strong);
1217
- last = m.index + m[0].length;
1218
- }
1219
- if (last < text.length) host.append(text.slice(last));
1220
- }
1221
- var inited = false;
1222
- var suppressed;
1223
- var SHOW_DELAY = 350;
1224
- var EDGE = 6;
1225
- var GAP = 7;
1226
- var tip = null;
1227
- var timer = 0;
1228
- var active = null;
1229
- var activeKey = null;
1230
- function ensureEl() {
1231
- if (tip) return tip;
1232
- const el2 = document.createElement("div");
1233
- el2.className = "tooltip";
1234
- el2.setAttribute("role", "tooltip");
1235
- el2.hidden = true;
1236
- document.body.appendChild(el2);
1237
- tip = el2;
1238
- return el2;
1239
- }
1240
- var rectOf = (anchor) => anchor instanceof HTMLElement ? anchor.getBoundingClientRect() : anchor;
1241
- function place(anchor) {
1242
- const t = ensureEl();
1243
- const r = rectOf(anchor);
1244
- const tr = t.getBoundingClientRect();
1245
- let top = r.top - tr.height - GAP;
1246
- if (top < EDGE) top = r.top + r.height + GAP;
1247
- let left = r.left + r.width / 2 - tr.width / 2;
1248
- left = Math.max(EDGE, Math.min(left, window.innerWidth - tr.width - EDGE));
1249
- t.style.left = `${Math.round(left)}px`;
1250
- t.style.top = `${Math.round(top)}px`;
1251
- }
1252
- function show(anchor) {
1253
- const text = anchor.dataset["tip"];
1254
- if (!text) return;
1255
- paint(anchor, text);
1256
- }
1257
- function paint(anchor, text) {
1258
- if (suppressed?.()) return;
1259
- const t = ensureEl();
1260
- const host = (anchor instanceof HTMLElement ? anchor.closest("dialog[open]") : null) ?? document.body;
1261
- if (t.parentElement !== host) host.appendChild(t);
1262
- active = anchor instanceof HTMLElement ? anchor : null;
1263
- renderTip(t, text);
1264
- t.hidden = false;
1265
- t.classList.remove("show");
1266
- void t.offsetWidth;
1267
- place(anchor);
1268
- t.classList.add("show");
1269
- }
1270
- function hide() {
1271
- if (timer) {
1272
- window.clearTimeout(timer);
1273
- timer = 0;
1274
- }
1275
- active = null;
1276
- activeKey = null;
1277
- if (tip) {
1278
- tip.hidden = true;
1279
- tip.classList.remove("show");
1280
- }
1281
- }
1282
- function schedule(anchor) {
1283
- if (anchor === active) return;
1284
- if (timer) window.clearTimeout(timer);
1285
- timer = window.setTimeout(() => show(anchor), SHOW_DELAY);
1286
- }
1287
- function tipAt(key, rect, text) {
1288
- if (key === activeKey) {
1289
- if (tip && !tip.hidden) {
1290
- renderTip(tip, text);
1291
- place(rect);
1292
- }
1293
- return;
1294
- }
1295
- hide();
1296
- activeKey = key;
1297
- timer = window.setTimeout(() => {
1298
- timer = 0;
1299
- paint(rect, text);
1300
- }, SHOW_DELAY);
1301
- }
1302
- function hideTip() {
1303
- hide();
1304
- }
1305
- var tipAncestor = (n) => n instanceof Element ? n.closest("[data-tip]") : null;
1306
- function initTooltips(opts = {}) {
1307
- if (inited) return;
1308
- inited = true;
1309
- suppressed = opts.suppressed;
1310
- document.addEventListener("pointerover", (e) => {
1311
- const el2 = tipAncestor(e.target);
1312
- if (el2) schedule(el2);
1313
- });
1314
- document.addEventListener("pointerout", (e) => {
1315
- const el2 = tipAncestor(e.target);
1316
- if (!el2) return;
1317
- const to = e.relatedTarget;
1318
- if (to instanceof Node && el2.contains(to)) return;
1319
- hide();
1320
- });
1321
- document.addEventListener("focusin", (e) => {
1322
- const el2 = tipAncestor(e.target);
1323
- if (el2) schedule(el2);
1324
- });
1325
- document.addEventListener("focusout", () => hide());
1326
- document.addEventListener("pointerdown", () => hide(), true);
1327
- document.addEventListener("scroll", () => {
1328
- if (active) hide();
1329
- }, true);
1330
- document.addEventListener("keydown", (e) => {
1331
- if (e.key === "Escape") hide();
1332
- });
1333
- window.addEventListener("blur", () => hide());
1334
- }
1335
-
1336
1371
  // src/save.ts
1337
1372
  function createSaveController(opts) {
1338
1373
  const delayMs = opts.delayMs ?? 700;
@@ -1524,6 +1559,8 @@ exports.JOB_PROGRESS = JOB_PROGRESS;
1524
1559
  exports.PALETTE = PALETTE;
1525
1560
  exports.PALETTE_SIZE = PALETTE_SIZE;
1526
1561
  exports.PANE_MENU = PANE_MENU;
1562
+ exports.PROPERTY_NAME_HINT = PROPERTY_NAME_HINT;
1563
+ exports.RESERVED_PROPERTY_NAMES = RESERVED_PROPERTY_NAMES;
1527
1564
  exports.askIdentity = askIdentity;
1528
1565
  exports.closeAnchoredPanel = closeAnchoredPanel;
1529
1566
  exports.closeWithExit = closeWithExit;
@@ -1542,7 +1579,9 @@ exports.icon = icon;
1542
1579
  exports.iconBtn = iconBtn;
1543
1580
  exports.iconSvg = iconSvg;
1544
1581
  exports.initTooltips = initTooltips;
1582
+ exports.isCaseOnlyPropertyName = isCaseOnlyPropertyName;
1545
1583
  exports.isValidGameId = isValidGameId;
1584
+ exports.isValidPropertyName = isValidPropertyName;
1546
1585
  exports.labelled = labelled;
1547
1586
  exports.lockControls = lockControls;
1548
1587
  exports.lockNotice = lockNotice;
@@ -1557,6 +1596,7 @@ exports.openNotesEditor = openNotesEditor;
1557
1596
  exports.paintVcBadges = paintVcBadges;
1558
1597
  exports.pinButton = pinButton;
1559
1598
  exports.placeAnchored = placeAnchored;
1599
+ exports.propertyNameify = propertyNameify;
1560
1600
  exports.renderStepperBar = renderStepperBar;
1561
1601
  exports.saveIndicator = saveIndicator;
1562
1602
  exports.showAbout = showAbout;