@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 +175 -135
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -2
- package/dist/index.d.ts +53 -2
- package/dist/index.js +171 -136
- package/dist/index.js.map +1 -1
- package/dist/settings.css +4 -0
- package/dist/settings.css.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -339,6 +339,46 @@ declare function gameIdify(text: string): string;
|
|
|
339
339
|
* "derive one", which is a different thing from an address. */
|
|
340
340
|
declare function isValidGameId(gameId: string): boolean;
|
|
341
341
|
|
|
342
|
+
/** The five words `@wildwinter/expr` lexes as keywords, so a property may not be
|
|
343
|
+
* called any of them: the reference does not resolve, it fails to parse. Kept
|
|
344
|
+
* here as a copy for the same reason the rules are (no dependency), and each
|
|
345
|
+
* app probes the real parser to prove the list is still true. */
|
|
346
|
+
declare const RESERVED_PROPERTY_NAMES: readonly string[];
|
|
347
|
+
/** What an editor should tell an author, in one line. */
|
|
348
|
+
declare const PROPERTY_NAME_HINT = "Lower case letters, digits and underscores. Cannot start with a digit. No hyphens or spaces.";
|
|
349
|
+
/**
|
|
350
|
+
* Coerce a label into a legal property name: lower case, apostrophes dropped,
|
|
351
|
+
* runs of anything else to a single underscore, no trailing underscore, an
|
|
352
|
+
* underscore in front if it would otherwise start with a digit, and one behind
|
|
353
|
+
* if it would otherwise be a keyword.
|
|
354
|
+
*
|
|
355
|
+
* A LEADING underscore is kept where the author typed one, because `_private` is
|
|
356
|
+
* a legal name and a meaningful choice; only the ends that would be illegal are
|
|
357
|
+
* touched.
|
|
358
|
+
*
|
|
359
|
+
* May return "", when there was nothing usable in the input. Callers treat that
|
|
360
|
+
* as "no name yet" rather than as a name.
|
|
361
|
+
*/
|
|
362
|
+
declare function propertyNameify(text: string): string;
|
|
363
|
+
/**
|
|
364
|
+
* Is this a name the expression language can actually reach? Lower case letters,
|
|
365
|
+
* digits and underscores, not starting with a digit, and not a keyword.
|
|
366
|
+
*
|
|
367
|
+
* "" is NOT legal: an unnamed declaration is an unfinished row, which is a
|
|
368
|
+
* different thing from an illegal one, and callers distinguish them.
|
|
369
|
+
*/
|
|
370
|
+
declare function isValidPropertyName(name: string): boolean;
|
|
371
|
+
/**
|
|
372
|
+
* The one thing that is only a case difference, e.g. `isNight` -> `isnight`.
|
|
373
|
+
*
|
|
374
|
+
* Worth its own question because it is the only violation that can be repaired
|
|
375
|
+
* without guessing at intent: the language already folds every REFERENCE, so
|
|
376
|
+
* folding the declaration to match changes nothing observable, where inventing
|
|
377
|
+
* an underscore for a space is a decision about what the author meant. Loaders
|
|
378
|
+
* fold this class silently and report the rest.
|
|
379
|
+
*/
|
|
380
|
+
declare function isCaseOnlyPropertyName(name: string): boolean;
|
|
381
|
+
|
|
342
382
|
interface GameIdEditorOptions {
|
|
343
383
|
anchor: HTMLElement;
|
|
344
384
|
/** The pinned address, or "" when it is being derived. */
|
|
@@ -568,7 +608,18 @@ interface TipRect {
|
|
|
568
608
|
declare function tipAt(key: string, rect: TipRect, text: string): void;
|
|
569
609
|
/** Take any tip down: the pointer has left, or a gesture has started. */
|
|
570
610
|
declare function hideTip(): void;
|
|
571
|
-
/**
|
|
611
|
+
/**
|
|
612
|
+
* Wire the one delegated controller. Mounting happens once; OPTIONS do not.
|
|
613
|
+
*
|
|
614
|
+
* An explicit call is always authoritative about its options, even after something else has already
|
|
615
|
+
* mounted the listener. That ordering is not hypothetical, and Storyletter caught it before it shipped:
|
|
616
|
+
* once components self-mount (see `ensureTooltipHost`), a host's own
|
|
617
|
+
* `initTooltips({ suppressed })` could land second and be dropped by an early return, silently taking
|
|
618
|
+
* a rule like "no tooltips in Writing View" with it. Nothing would fail and nothing would show.
|
|
619
|
+
*
|
|
620
|
+
* Components therefore call this with NO arguments: they mount the renderer without claiming the
|
|
621
|
+
* options, and the host keeps the last word on behaviour.
|
|
622
|
+
*/
|
|
572
623
|
declare function initTooltips(opts?: TooltipOptions): void;
|
|
573
624
|
|
|
574
625
|
/**
|
|
@@ -708,4 +759,4 @@ interface StepperBarOptions {
|
|
|
708
759
|
/** Draw (or hide) the bar in `host`. Idempotent: call it again with new state. */
|
|
709
760
|
declare function renderStepperBar(host: HTMLElement, opts: StepperBarOptions): void;
|
|
710
761
|
|
|
711
|
-
export { type AboutOptions, type AnchoredPanel, type AnchoredPanelOptions, type Child, type Comment, type CommentMessage, type CommentsOptions, type ConfirmOptions, type DocLine, type DupGuard, type ElProps, type GameIdEditorOptions, type IconName, type IconSvgName, type Identity, type IdentityOptions, type JobProgressOptions, type JobProgressView, type Notes, type NotesEditorOptions, PALETTE, PALETTE_SIZE, type PaneShell, type PaneShellOptions, type PaneShellState, type PaneSide, type PaneSideConfig, type PinButton, type PinButtonOptions, type SaveController, type SaveControllerOptions, type SaveIndicator, type SaveStatus, type SettingsDialog, type SettingsDialogOptions, type SettingsSection, type SettingsSectionHandle, type ShardVc, type StaleBarOptions, type StepperBarOptions, type StepperItem, type StepperTone, type TipRect, type TooltipOptions, type VcMap, askIdentity, closeAnchoredPanel, closeWithExit, colourFor, colourIndex, confirmDialog, createSaveController, dupGuard, el, expandableRow, focusNewRow, foldVc, gameIdify, hideTip, icon, iconBtn, iconSvg, initTooltips, isValidGameId, labelled, lockControls, lockNotice, mountJobProgress, mountPaneShell, mountSettingsDialog, moveItem, openAnchoredPanel, openComments, openGameIdEditor, openNotesEditor, paintVcBadges, pinButton, placeAnchored, renderStepperBar, saveIndicator, showAbout, staleBar, tagChips, tipAt, tipBold, vcBadgeFor };
|
|
762
|
+
export { type AboutOptions, type AnchoredPanel, type AnchoredPanelOptions, type Child, type Comment, type CommentMessage, type CommentsOptions, type ConfirmOptions, type DocLine, type DupGuard, type ElProps, type GameIdEditorOptions, type IconName, type IconSvgName, type Identity, type IdentityOptions, type JobProgressOptions, type JobProgressView, type Notes, type NotesEditorOptions, PALETTE, PALETTE_SIZE, PROPERTY_NAME_HINT, type PaneShell, type PaneShellOptions, type PaneShellState, type PaneSide, type PaneSideConfig, type PinButton, type PinButtonOptions, RESERVED_PROPERTY_NAMES, type SaveController, type SaveControllerOptions, type SaveIndicator, type SaveStatus, type SettingsDialog, type SettingsDialogOptions, type SettingsSection, type SettingsSectionHandle, type ShardVc, type StaleBarOptions, type StepperBarOptions, type StepperItem, type StepperTone, type TipRect, type TooltipOptions, type VcMap, askIdentity, closeAnchoredPanel, closeWithExit, colourFor, colourIndex, confirmDialog, createSaveController, dupGuard, el, expandableRow, focusNewRow, foldVc, gameIdify, hideTip, icon, iconBtn, iconSvg, initTooltips, isCaseOnlyPropertyName, isValidGameId, isValidPropertyName, labelled, lockControls, lockNotice, mountJobProgress, mountPaneShell, mountSettingsDialog, moveItem, openAnchoredPanel, openComments, openGameIdEditor, openNotesEditor, paintVcBadges, pinButton, placeAnchored, propertyNameify, renderStepperBar, saveIndicator, showAbout, staleBar, tagChips, tipAt, tipBold, vcBadgeFor };
|
package/dist/index.d.ts
CHANGED
|
@@ -339,6 +339,46 @@ declare function gameIdify(text: string): string;
|
|
|
339
339
|
* "derive one", which is a different thing from an address. */
|
|
340
340
|
declare function isValidGameId(gameId: string): boolean;
|
|
341
341
|
|
|
342
|
+
/** The five words `@wildwinter/expr` lexes as keywords, so a property may not be
|
|
343
|
+
* called any of them: the reference does not resolve, it fails to parse. Kept
|
|
344
|
+
* here as a copy for the same reason the rules are (no dependency), and each
|
|
345
|
+
* app probes the real parser to prove the list is still true. */
|
|
346
|
+
declare const RESERVED_PROPERTY_NAMES: readonly string[];
|
|
347
|
+
/** What an editor should tell an author, in one line. */
|
|
348
|
+
declare const PROPERTY_NAME_HINT = "Lower case letters, digits and underscores. Cannot start with a digit. No hyphens or spaces.";
|
|
349
|
+
/**
|
|
350
|
+
* Coerce a label into a legal property name: lower case, apostrophes dropped,
|
|
351
|
+
* runs of anything else to a single underscore, no trailing underscore, an
|
|
352
|
+
* underscore in front if it would otherwise start with a digit, and one behind
|
|
353
|
+
* if it would otherwise be a keyword.
|
|
354
|
+
*
|
|
355
|
+
* A LEADING underscore is kept where the author typed one, because `_private` is
|
|
356
|
+
* a legal name and a meaningful choice; only the ends that would be illegal are
|
|
357
|
+
* touched.
|
|
358
|
+
*
|
|
359
|
+
* May return "", when there was nothing usable in the input. Callers treat that
|
|
360
|
+
* as "no name yet" rather than as a name.
|
|
361
|
+
*/
|
|
362
|
+
declare function propertyNameify(text: string): string;
|
|
363
|
+
/**
|
|
364
|
+
* Is this a name the expression language can actually reach? Lower case letters,
|
|
365
|
+
* digits and underscores, not starting with a digit, and not a keyword.
|
|
366
|
+
*
|
|
367
|
+
* "" is NOT legal: an unnamed declaration is an unfinished row, which is a
|
|
368
|
+
* different thing from an illegal one, and callers distinguish them.
|
|
369
|
+
*/
|
|
370
|
+
declare function isValidPropertyName(name: string): boolean;
|
|
371
|
+
/**
|
|
372
|
+
* The one thing that is only a case difference, e.g. `isNight` -> `isnight`.
|
|
373
|
+
*
|
|
374
|
+
* Worth its own question because it is the only violation that can be repaired
|
|
375
|
+
* without guessing at intent: the language already folds every REFERENCE, so
|
|
376
|
+
* folding the declaration to match changes nothing observable, where inventing
|
|
377
|
+
* an underscore for a space is a decision about what the author meant. Loaders
|
|
378
|
+
* fold this class silently and report the rest.
|
|
379
|
+
*/
|
|
380
|
+
declare function isCaseOnlyPropertyName(name: string): boolean;
|
|
381
|
+
|
|
342
382
|
interface GameIdEditorOptions {
|
|
343
383
|
anchor: HTMLElement;
|
|
344
384
|
/** The pinned address, or "" when it is being derived. */
|
|
@@ -568,7 +608,18 @@ interface TipRect {
|
|
|
568
608
|
declare function tipAt(key: string, rect: TipRect, text: string): void;
|
|
569
609
|
/** Take any tip down: the pointer has left, or a gesture has started. */
|
|
570
610
|
declare function hideTip(): void;
|
|
571
|
-
/**
|
|
611
|
+
/**
|
|
612
|
+
* Wire the one delegated controller. Mounting happens once; OPTIONS do not.
|
|
613
|
+
*
|
|
614
|
+
* An explicit call is always authoritative about its options, even after something else has already
|
|
615
|
+
* mounted the listener. That ordering is not hypothetical, and Storyletter caught it before it shipped:
|
|
616
|
+
* once components self-mount (see `ensureTooltipHost`), a host's own
|
|
617
|
+
* `initTooltips({ suppressed })` could land second and be dropped by an early return, silently taking
|
|
618
|
+
* a rule like "no tooltips in Writing View" with it. Nothing would fail and nothing would show.
|
|
619
|
+
*
|
|
620
|
+
* Components therefore call this with NO arguments: they mount the renderer without claiming the
|
|
621
|
+
* options, and the host keeps the last word on behaviour.
|
|
622
|
+
*/
|
|
572
623
|
declare function initTooltips(opts?: TooltipOptions): void;
|
|
573
624
|
|
|
574
625
|
/**
|
|
@@ -708,4 +759,4 @@ interface StepperBarOptions {
|
|
|
708
759
|
/** Draw (or hide) the bar in `host`. Idempotent: call it again with new state. */
|
|
709
760
|
declare function renderStepperBar(host: HTMLElement, opts: StepperBarOptions): void;
|
|
710
761
|
|
|
711
|
-
export { type AboutOptions, type AnchoredPanel, type AnchoredPanelOptions, type Child, type Comment, type CommentMessage, type CommentsOptions, type ConfirmOptions, type DocLine, type DupGuard, type ElProps, type GameIdEditorOptions, type IconName, type IconSvgName, type Identity, type IdentityOptions, type JobProgressOptions, type JobProgressView, type Notes, type NotesEditorOptions, PALETTE, PALETTE_SIZE, type PaneShell, type PaneShellOptions, type PaneShellState, type PaneSide, type PaneSideConfig, type PinButton, type PinButtonOptions, type SaveController, type SaveControllerOptions, type SaveIndicator, type SaveStatus, type SettingsDialog, type SettingsDialogOptions, type SettingsSection, type SettingsSectionHandle, type ShardVc, type StaleBarOptions, type StepperBarOptions, type StepperItem, type StepperTone, type TipRect, type TooltipOptions, type VcMap, askIdentity, closeAnchoredPanel, closeWithExit, colourFor, colourIndex, confirmDialog, createSaveController, dupGuard, el, expandableRow, focusNewRow, foldVc, gameIdify, hideTip, icon, iconBtn, iconSvg, initTooltips, isValidGameId, labelled, lockControls, lockNotice, mountJobProgress, mountPaneShell, mountSettingsDialog, moveItem, openAnchoredPanel, openComments, openGameIdEditor, openNotesEditor, paintVcBadges, pinButton, placeAnchored, renderStepperBar, saveIndicator, showAbout, staleBar, tagChips, tipAt, tipBold, vcBadgeFor };
|
|
762
|
+
export { type AboutOptions, type AnchoredPanel, type AnchoredPanelOptions, type Child, type Comment, type CommentMessage, type CommentsOptions, type ConfirmOptions, type DocLine, type DupGuard, type ElProps, type GameIdEditorOptions, type IconName, type IconSvgName, type Identity, type IdentityOptions, type JobProgressOptions, type JobProgressView, type Notes, type NotesEditorOptions, PALETTE, PALETTE_SIZE, PROPERTY_NAME_HINT, type PaneShell, type PaneShellOptions, type PaneShellState, type PaneSide, type PaneSideConfig, type PinButton, type PinButtonOptions, RESERVED_PROPERTY_NAMES, type SaveController, type SaveControllerOptions, type SaveIndicator, type SaveStatus, type SettingsDialog, type SettingsDialogOptions, type SettingsSection, type SettingsSectionHandle, type ShardVc, type StaleBarOptions, type StepperBarOptions, type StepperItem, type StepperTone, type TipRect, type TooltipOptions, type VcMap, askIdentity, closeAnchoredPanel, closeWithExit, colourFor, colourIndex, confirmDialog, createSaveController, dupGuard, el, expandableRow, focusNewRow, foldVc, gameIdify, hideTip, icon, iconBtn, iconSvg, initTooltips, isCaseOnlyPropertyName, isValidGameId, isValidPropertyName, labelled, lockControls, lockNotice, mountJobProgress, mountPaneShell, mountSettingsDialog, moveItem, openAnchoredPanel, openComments, openGameIdEditor, openNotesEditor, paintVcBadges, pinButton, placeAnchored, propertyNameify, renderStepperBar, saveIndicator, showAbout, staleBar, tagChips, tipAt, tipBold, vcBadgeFor };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,155 @@
|
|
|
1
1
|
export { JOB_PROGRESS } from './chunk-MDGOIT3O.js';
|
|
2
2
|
export { EDIT_MENU, PANE_MENU } from './chunk-YLINCOQD.js';
|
|
3
3
|
|
|
4
|
+
// src/tooltip.ts
|
|
5
|
+
var B_OPEN = "";
|
|
6
|
+
var B_CLOSE = "";
|
|
7
|
+
function tipBold(s) {
|
|
8
|
+
return `${B_OPEN}${s}${B_CLOSE}`;
|
|
9
|
+
}
|
|
10
|
+
function renderTip(host, text) {
|
|
11
|
+
host.textContent = "";
|
|
12
|
+
const re = /\u0002([^\u0003]*)\u0003/g;
|
|
13
|
+
let last = 0;
|
|
14
|
+
let m;
|
|
15
|
+
while ((m = re.exec(text)) !== null) {
|
|
16
|
+
if (m.index > last) host.append(text.slice(last, m.index));
|
|
17
|
+
const strong = document.createElement("strong");
|
|
18
|
+
strong.textContent = m[1] ?? "";
|
|
19
|
+
host.append(strong);
|
|
20
|
+
last = m.index + m[0].length;
|
|
21
|
+
}
|
|
22
|
+
if (last < text.length) host.append(text.slice(last));
|
|
23
|
+
}
|
|
24
|
+
var inited = false;
|
|
25
|
+
var checked = false;
|
|
26
|
+
var suppressed;
|
|
27
|
+
var SHOW_DELAY = 350;
|
|
28
|
+
var EDGE = 6;
|
|
29
|
+
var GAP = 7;
|
|
30
|
+
var tip = null;
|
|
31
|
+
var timer = 0;
|
|
32
|
+
var active = null;
|
|
33
|
+
var activeKey = null;
|
|
34
|
+
function ensureEl() {
|
|
35
|
+
if (tip) return tip;
|
|
36
|
+
const el2 = document.createElement("div");
|
|
37
|
+
el2.className = "tooltip";
|
|
38
|
+
el2.setAttribute("role", "tooltip");
|
|
39
|
+
el2.hidden = true;
|
|
40
|
+
document.body.appendChild(el2);
|
|
41
|
+
tip = el2;
|
|
42
|
+
return el2;
|
|
43
|
+
}
|
|
44
|
+
var rectOf = (anchor) => anchor instanceof HTMLElement ? anchor.getBoundingClientRect() : anchor;
|
|
45
|
+
function place(anchor) {
|
|
46
|
+
const t = ensureEl();
|
|
47
|
+
const r = rectOf(anchor);
|
|
48
|
+
const tr = t.getBoundingClientRect();
|
|
49
|
+
let top = r.top - tr.height - GAP;
|
|
50
|
+
if (top < EDGE) top = r.top + r.height + GAP;
|
|
51
|
+
let left = r.left + r.width / 2 - tr.width / 2;
|
|
52
|
+
left = Math.max(EDGE, Math.min(left, window.innerWidth - tr.width - EDGE));
|
|
53
|
+
t.style.left = `${Math.round(left)}px`;
|
|
54
|
+
t.style.top = `${Math.round(top)}px`;
|
|
55
|
+
}
|
|
56
|
+
function show(anchor) {
|
|
57
|
+
const text = anchor.dataset["tip"];
|
|
58
|
+
if (!text) return;
|
|
59
|
+
paint(anchor, text);
|
|
60
|
+
}
|
|
61
|
+
function paint(anchor, text) {
|
|
62
|
+
if (suppressed?.()) return;
|
|
63
|
+
const t = ensureEl();
|
|
64
|
+
const host = (anchor instanceof HTMLElement ? anchor.closest("dialog[open]") : null) ?? document.body;
|
|
65
|
+
if (t.parentElement !== host) host.appendChild(t);
|
|
66
|
+
active = anchor instanceof HTMLElement ? anchor : null;
|
|
67
|
+
renderTip(t, text);
|
|
68
|
+
t.hidden = false;
|
|
69
|
+
t.classList.remove("show");
|
|
70
|
+
void t.offsetWidth;
|
|
71
|
+
place(anchor);
|
|
72
|
+
t.classList.add("show");
|
|
73
|
+
}
|
|
74
|
+
function hide() {
|
|
75
|
+
if (timer) {
|
|
76
|
+
window.clearTimeout(timer);
|
|
77
|
+
timer = 0;
|
|
78
|
+
}
|
|
79
|
+
active = null;
|
|
80
|
+
activeKey = null;
|
|
81
|
+
if (tip) {
|
|
82
|
+
tip.hidden = true;
|
|
83
|
+
tip.classList.remove("show");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function schedule(anchor) {
|
|
87
|
+
if (anchor === active) return;
|
|
88
|
+
if (timer) window.clearTimeout(timer);
|
|
89
|
+
timer = window.setTimeout(() => show(anchor), SHOW_DELAY);
|
|
90
|
+
}
|
|
91
|
+
function tipAt(key, rect, text) {
|
|
92
|
+
if (key === activeKey) {
|
|
93
|
+
if (tip && !tip.hidden) {
|
|
94
|
+
renderTip(tip, text);
|
|
95
|
+
place(rect);
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
hide();
|
|
100
|
+
activeKey = key;
|
|
101
|
+
timer = window.setTimeout(() => {
|
|
102
|
+
timer = 0;
|
|
103
|
+
paint(rect, text);
|
|
104
|
+
}, SHOW_DELAY);
|
|
105
|
+
}
|
|
106
|
+
function hideTip() {
|
|
107
|
+
hide();
|
|
108
|
+
}
|
|
109
|
+
function ensureTooltipHost() {
|
|
110
|
+
initTooltips();
|
|
111
|
+
}
|
|
112
|
+
function checkTooltipHost() {
|
|
113
|
+
if (checked) return;
|
|
114
|
+
checked = true;
|
|
115
|
+
setTimeout(() => {
|
|
116
|
+
if (inited || typeof document === "undefined") return;
|
|
117
|
+
if (document.querySelector("[data-tip]")) {
|
|
118
|
+
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.");
|
|
119
|
+
}
|
|
120
|
+
}, 0);
|
|
121
|
+
}
|
|
122
|
+
var tipAncestor = (n) => n instanceof Element ? n.closest("[data-tip]") : null;
|
|
123
|
+
function initTooltips(opts = {}) {
|
|
124
|
+
if (opts.suppressed !== void 0) suppressed = opts.suppressed;
|
|
125
|
+
if (inited) return;
|
|
126
|
+
inited = true;
|
|
127
|
+
document.addEventListener("pointerover", (e) => {
|
|
128
|
+
const el2 = tipAncestor(e.target);
|
|
129
|
+
if (el2) schedule(el2);
|
|
130
|
+
});
|
|
131
|
+
document.addEventListener("pointerout", (e) => {
|
|
132
|
+
const el2 = tipAncestor(e.target);
|
|
133
|
+
if (!el2) return;
|
|
134
|
+
const to = e.relatedTarget;
|
|
135
|
+
if (to instanceof Node && el2.contains(to)) return;
|
|
136
|
+
hide();
|
|
137
|
+
});
|
|
138
|
+
document.addEventListener("focusin", (e) => {
|
|
139
|
+
const el2 = tipAncestor(e.target);
|
|
140
|
+
if (el2) schedule(el2);
|
|
141
|
+
});
|
|
142
|
+
document.addEventListener("focusout", () => hide());
|
|
143
|
+
document.addEventListener("pointerdown", () => hide(), true);
|
|
144
|
+
document.addEventListener("scroll", () => {
|
|
145
|
+
if (active) hide();
|
|
146
|
+
}, true);
|
|
147
|
+
document.addEventListener("keydown", (e) => {
|
|
148
|
+
if (e.key === "Escape") hide();
|
|
149
|
+
});
|
|
150
|
+
window.addEventListener("blur", () => hide());
|
|
151
|
+
}
|
|
152
|
+
|
|
4
153
|
// src/dom.ts
|
|
5
154
|
function el(tag, classNameOrProps, ...children) {
|
|
6
155
|
const node = document.createElement(tag);
|
|
@@ -11,12 +160,14 @@ function el(tag, classNameOrProps, ...children) {
|
|
|
11
160
|
if (props.tip !== void 0) {
|
|
12
161
|
node.dataset["tip"] = props.tip;
|
|
13
162
|
node.setAttribute("aria-label", props.tip);
|
|
163
|
+
ensureTooltipHost();
|
|
14
164
|
}
|
|
15
165
|
if (props.onClick) node.addEventListener("click", props.onClick);
|
|
16
166
|
for (const child of children) {
|
|
17
167
|
if (child === null || child === void 0) continue;
|
|
18
168
|
node.append(child);
|
|
19
169
|
}
|
|
170
|
+
checkTooltipHost();
|
|
20
171
|
return node;
|
|
21
172
|
}
|
|
22
173
|
function iconBtn(glyph, title, onClick, disabled = false, danger = false) {
|
|
@@ -709,6 +860,25 @@ function isValidGameId(gameId) {
|
|
|
709
860
|
return /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(gameId);
|
|
710
861
|
}
|
|
711
862
|
|
|
863
|
+
// src/property-names.ts
|
|
864
|
+
var RESERVED_PROPERTY_NAMES = ["true", "false", "and", "or", "not"];
|
|
865
|
+
var PROPERTY_NAME_HINT = "Lower case letters, digits and underscores. Cannot start with a digit. No hyphens or spaces.";
|
|
866
|
+
function propertyNameify(text) {
|
|
867
|
+
const trimmed = text.trim();
|
|
868
|
+
const deliberateLeading = trimmed.startsWith("_");
|
|
869
|
+
let out = trimmed.toLowerCase().replace(/['’]/g, "").replace(/[^a-z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
870
|
+
if (out === "") return "";
|
|
871
|
+
if (deliberateLeading || /^[0-9]/.test(out)) out = `_${out}`;
|
|
872
|
+
if (RESERVED_PROPERTY_NAMES.includes(out)) out = `${out}_`;
|
|
873
|
+
return out;
|
|
874
|
+
}
|
|
875
|
+
function isValidPropertyName(name) {
|
|
876
|
+
return /^[a-z_][a-z0-9_]*$/.test(name) && !RESERVED_PROPERTY_NAMES.includes(name);
|
|
877
|
+
}
|
|
878
|
+
function isCaseOnlyPropertyName(name) {
|
|
879
|
+
return !isValidPropertyName(name) && isValidPropertyName(name.toLowerCase());
|
|
880
|
+
}
|
|
881
|
+
|
|
712
882
|
// src/id-editor.ts
|
|
713
883
|
function openGameIdEditor(opts) {
|
|
714
884
|
const slugify = opts.slugify ?? gameIdify;
|
|
@@ -1183,141 +1353,6 @@ function mountJobProgress(host, opts) {
|
|
|
1183
1353
|
return view;
|
|
1184
1354
|
}
|
|
1185
1355
|
|
|
1186
|
-
// src/tooltip.ts
|
|
1187
|
-
var B_OPEN = "";
|
|
1188
|
-
var B_CLOSE = "";
|
|
1189
|
-
function tipBold(s) {
|
|
1190
|
-
return `${B_OPEN}${s}${B_CLOSE}`;
|
|
1191
|
-
}
|
|
1192
|
-
function renderTip(host, text) {
|
|
1193
|
-
host.textContent = "";
|
|
1194
|
-
const re = /\u0002([^\u0003]*)\u0003/g;
|
|
1195
|
-
let last = 0;
|
|
1196
|
-
let m;
|
|
1197
|
-
while ((m = re.exec(text)) !== null) {
|
|
1198
|
-
if (m.index > last) host.append(text.slice(last, m.index));
|
|
1199
|
-
const strong = document.createElement("strong");
|
|
1200
|
-
strong.textContent = m[1] ?? "";
|
|
1201
|
-
host.append(strong);
|
|
1202
|
-
last = m.index + m[0].length;
|
|
1203
|
-
}
|
|
1204
|
-
if (last < text.length) host.append(text.slice(last));
|
|
1205
|
-
}
|
|
1206
|
-
var inited = false;
|
|
1207
|
-
var suppressed;
|
|
1208
|
-
var SHOW_DELAY = 350;
|
|
1209
|
-
var EDGE = 6;
|
|
1210
|
-
var GAP = 7;
|
|
1211
|
-
var tip = null;
|
|
1212
|
-
var timer = 0;
|
|
1213
|
-
var active = null;
|
|
1214
|
-
var activeKey = null;
|
|
1215
|
-
function ensureEl() {
|
|
1216
|
-
if (tip) return tip;
|
|
1217
|
-
const el2 = document.createElement("div");
|
|
1218
|
-
el2.className = "tooltip";
|
|
1219
|
-
el2.setAttribute("role", "tooltip");
|
|
1220
|
-
el2.hidden = true;
|
|
1221
|
-
document.body.appendChild(el2);
|
|
1222
|
-
tip = el2;
|
|
1223
|
-
return el2;
|
|
1224
|
-
}
|
|
1225
|
-
var rectOf = (anchor) => anchor instanceof HTMLElement ? anchor.getBoundingClientRect() : anchor;
|
|
1226
|
-
function place(anchor) {
|
|
1227
|
-
const t = ensureEl();
|
|
1228
|
-
const r = rectOf(anchor);
|
|
1229
|
-
const tr = t.getBoundingClientRect();
|
|
1230
|
-
let top = r.top - tr.height - GAP;
|
|
1231
|
-
if (top < EDGE) top = r.top + r.height + GAP;
|
|
1232
|
-
let left = r.left + r.width / 2 - tr.width / 2;
|
|
1233
|
-
left = Math.max(EDGE, Math.min(left, window.innerWidth - tr.width - EDGE));
|
|
1234
|
-
t.style.left = `${Math.round(left)}px`;
|
|
1235
|
-
t.style.top = `${Math.round(top)}px`;
|
|
1236
|
-
}
|
|
1237
|
-
function show(anchor) {
|
|
1238
|
-
const text = anchor.dataset["tip"];
|
|
1239
|
-
if (!text) return;
|
|
1240
|
-
paint(anchor, text);
|
|
1241
|
-
}
|
|
1242
|
-
function paint(anchor, text) {
|
|
1243
|
-
if (suppressed?.()) return;
|
|
1244
|
-
const t = ensureEl();
|
|
1245
|
-
const host = (anchor instanceof HTMLElement ? anchor.closest("dialog[open]") : null) ?? document.body;
|
|
1246
|
-
if (t.parentElement !== host) host.appendChild(t);
|
|
1247
|
-
active = anchor instanceof HTMLElement ? anchor : null;
|
|
1248
|
-
renderTip(t, text);
|
|
1249
|
-
t.hidden = false;
|
|
1250
|
-
t.classList.remove("show");
|
|
1251
|
-
void t.offsetWidth;
|
|
1252
|
-
place(anchor);
|
|
1253
|
-
t.classList.add("show");
|
|
1254
|
-
}
|
|
1255
|
-
function hide() {
|
|
1256
|
-
if (timer) {
|
|
1257
|
-
window.clearTimeout(timer);
|
|
1258
|
-
timer = 0;
|
|
1259
|
-
}
|
|
1260
|
-
active = null;
|
|
1261
|
-
activeKey = null;
|
|
1262
|
-
if (tip) {
|
|
1263
|
-
tip.hidden = true;
|
|
1264
|
-
tip.classList.remove("show");
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
function schedule(anchor) {
|
|
1268
|
-
if (anchor === active) return;
|
|
1269
|
-
if (timer) window.clearTimeout(timer);
|
|
1270
|
-
timer = window.setTimeout(() => show(anchor), SHOW_DELAY);
|
|
1271
|
-
}
|
|
1272
|
-
function tipAt(key, rect, text) {
|
|
1273
|
-
if (key === activeKey) {
|
|
1274
|
-
if (tip && !tip.hidden) {
|
|
1275
|
-
renderTip(tip, text);
|
|
1276
|
-
place(rect);
|
|
1277
|
-
}
|
|
1278
|
-
return;
|
|
1279
|
-
}
|
|
1280
|
-
hide();
|
|
1281
|
-
activeKey = key;
|
|
1282
|
-
timer = window.setTimeout(() => {
|
|
1283
|
-
timer = 0;
|
|
1284
|
-
paint(rect, text);
|
|
1285
|
-
}, SHOW_DELAY);
|
|
1286
|
-
}
|
|
1287
|
-
function hideTip() {
|
|
1288
|
-
hide();
|
|
1289
|
-
}
|
|
1290
|
-
var tipAncestor = (n) => n instanceof Element ? n.closest("[data-tip]") : null;
|
|
1291
|
-
function initTooltips(opts = {}) {
|
|
1292
|
-
if (inited) return;
|
|
1293
|
-
inited = true;
|
|
1294
|
-
suppressed = opts.suppressed;
|
|
1295
|
-
document.addEventListener("pointerover", (e) => {
|
|
1296
|
-
const el2 = tipAncestor(e.target);
|
|
1297
|
-
if (el2) schedule(el2);
|
|
1298
|
-
});
|
|
1299
|
-
document.addEventListener("pointerout", (e) => {
|
|
1300
|
-
const el2 = tipAncestor(e.target);
|
|
1301
|
-
if (!el2) return;
|
|
1302
|
-
const to = e.relatedTarget;
|
|
1303
|
-
if (to instanceof Node && el2.contains(to)) return;
|
|
1304
|
-
hide();
|
|
1305
|
-
});
|
|
1306
|
-
document.addEventListener("focusin", (e) => {
|
|
1307
|
-
const el2 = tipAncestor(e.target);
|
|
1308
|
-
if (el2) schedule(el2);
|
|
1309
|
-
});
|
|
1310
|
-
document.addEventListener("focusout", () => hide());
|
|
1311
|
-
document.addEventListener("pointerdown", () => hide(), true);
|
|
1312
|
-
document.addEventListener("scroll", () => {
|
|
1313
|
-
if (active) hide();
|
|
1314
|
-
}, true);
|
|
1315
|
-
document.addEventListener("keydown", (e) => {
|
|
1316
|
-
if (e.key === "Escape") hide();
|
|
1317
|
-
});
|
|
1318
|
-
window.addEventListener("blur", () => hide());
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
1356
|
// src/save.ts
|
|
1322
1357
|
function createSaveController(opts) {
|
|
1323
1358
|
const delayMs = opts.delayMs ?? 700;
|
|
@@ -1504,6 +1539,6 @@ function renderStepperBar(host, opts) {
|
|
|
1504
1539
|
);
|
|
1505
1540
|
}
|
|
1506
1541
|
|
|
1507
|
-
export { PALETTE, PALETTE_SIZE, askIdentity, closeAnchoredPanel, closeWithExit, colourFor, colourIndex, confirmDialog, createSaveController, dupGuard, el, expandableRow, focusNewRow, foldVc, gameIdify, hideTip, icon, iconBtn, iconSvg, initTooltips, isValidGameId, labelled, lockControls, lockNotice, mountJobProgress, mountPaneShell, mountSettingsDialog, moveItem, openAnchoredPanel, openComments, openGameIdEditor, openNotesEditor, paintVcBadges, pinButton, placeAnchored, renderStepperBar, saveIndicator, showAbout, staleBar, tagChips, tipAt, tipBold, vcBadgeFor };
|
|
1542
|
+
export { PALETTE, PALETTE_SIZE, PROPERTY_NAME_HINT, RESERVED_PROPERTY_NAMES, askIdentity, closeAnchoredPanel, closeWithExit, colourFor, colourIndex, confirmDialog, createSaveController, dupGuard, el, expandableRow, focusNewRow, foldVc, gameIdify, hideTip, icon, iconBtn, iconSvg, initTooltips, isCaseOnlyPropertyName, isValidGameId, isValidPropertyName, labelled, lockControls, lockNotice, mountJobProgress, mountPaneShell, mountSettingsDialog, moveItem, openAnchoredPanel, openComments, openGameIdEditor, openNotesEditor, paintVcBadges, pinButton, placeAnchored, propertyNameify, renderStepperBar, saveIndicator, showAbout, staleBar, tagChips, tipAt, tipBold, vcBadgeFor };
|
|
1508
1543
|
//# sourceMappingURL=index.js.map
|
|
1509
1544
|
//# sourceMappingURL=index.js.map
|