@marianmeres/stuic 2.16.0 → 2.18.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/components/AvatarInitials/AvatarInitials.svelte +3 -2
- package/dist/components/AvatarInitials/AvatarInitials.svelte.d.ts +1 -1
- package/dist/components/Backdrop/Backdrop.svelte +26 -6
- package/dist/components/DropdownMenu/DropdownMenu.svelte +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.svelte.d.ts +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/input-history.svelte.d.ts +102 -0
- package/dist/utils/input-history.svelte.js +197 -0
- package/dist/utils/observe-exists.svelte.js +3 -3
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/** Optional string for color hash calculation (e.g., email, user ID). Falls back to `input` */
|
|
6
6
|
hashSource?: string;
|
|
7
7
|
/** Size preset or custom Tailwind size class */
|
|
8
|
-
size?: "sm" | "md" | "lg" | string;
|
|
8
|
+
size?: "sm" | "md" | "lg" | "xl" | string;
|
|
9
9
|
/** Click handler - when provided, renders as a button */
|
|
10
10
|
onclick?: (event: MouseEvent) => void;
|
|
11
11
|
/** Background color (Tailwind class). Ignored if autoColor=true */
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
sm: "size-8 text-xs",
|
|
42
42
|
md: "size-10 text-sm",
|
|
43
43
|
lg: "size-14 text-base",
|
|
44
|
+
xl: "size-16 text-lg",
|
|
44
45
|
};
|
|
45
46
|
|
|
46
47
|
let initials = $derived.by(() => {
|
|
@@ -52,7 +53,7 @@
|
|
|
52
53
|
if (_input.includes("@")) {
|
|
53
54
|
const username = _input.split("@")[0];
|
|
54
55
|
// Split by common separators (., _, -)
|
|
55
|
-
const parts = username.split(/[._
|
|
56
|
+
const parts = username.split(/[._+-]/).filter(Boolean);
|
|
56
57
|
if (parts.length > 1) {
|
|
57
58
|
_input = parts.map((p) => p.charAt(0)).join("");
|
|
58
59
|
} else {
|
|
@@ -4,7 +4,7 @@ export interface Props {
|
|
|
4
4
|
/** Optional string for color hash calculation (e.g., email, user ID). Falls back to `input` */
|
|
5
5
|
hashSource?: string;
|
|
6
6
|
/** Size preset or custom Tailwind size class */
|
|
7
|
-
size?: "sm" | "md" | "lg" | string;
|
|
7
|
+
size?: "sm" | "md" | "lg" | "xl" | string;
|
|
8
8
|
/** Click handler - when provided, renders as a button */
|
|
9
9
|
onclick?: (event: MouseEvent) => void;
|
|
10
10
|
/** Background color (Tailwind class). Ignored if autoColor=true */
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
visible?: boolean;
|
|
15
15
|
noScrollLock?: boolean;
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
// Stack to track visible Backdrops - only topmost handles Escape
|
|
19
|
+
const escapeStack: Set<symbol> = new Set();
|
|
17
20
|
</script>
|
|
18
21
|
|
|
19
22
|
<script lang="ts">
|
|
@@ -104,17 +107,34 @@
|
|
|
104
107
|
// Note, that this will also reset if nested... (which is not desired, but ignoring)
|
|
105
108
|
onDestroy(BodyScroll.unlock);
|
|
106
109
|
|
|
110
|
+
// Unique ID for this Backdrop instance
|
|
111
|
+
const instanceId = Symbol();
|
|
112
|
+
|
|
107
113
|
$effect(() => {
|
|
114
|
+
if (!visible || typeof onEscape !== "function") return;
|
|
115
|
+
|
|
116
|
+
// Add to stack when visible
|
|
117
|
+
escapeStack.add(instanceId);
|
|
118
|
+
|
|
108
119
|
function onkeydown(e: KeyboardEvent) {
|
|
109
|
-
if
|
|
110
|
-
|
|
111
|
-
|
|
120
|
+
// Skip if already handled by another component (ModalDialog, DropdownMenu, etc.)
|
|
121
|
+
if (e.defaultPrevented) return;
|
|
122
|
+
|
|
123
|
+
// Only handle if this is the topmost Backdrop
|
|
124
|
+
const stack = [...escapeStack];
|
|
125
|
+
if (stack[stack.length - 1] !== instanceId) return;
|
|
126
|
+
|
|
127
|
+
if (e.key === "Escape") {
|
|
112
128
|
e.preventDefault();
|
|
113
|
-
onEscape();
|
|
129
|
+
onEscape?.();
|
|
114
130
|
}
|
|
115
131
|
}
|
|
116
|
-
|
|
117
|
-
|
|
132
|
+
|
|
133
|
+
window.addEventListener("keydown", onkeydown);
|
|
134
|
+
return () => {
|
|
135
|
+
escapeStack.delete(instanceId);
|
|
136
|
+
window.removeEventListener("keydown", onkeydown);
|
|
137
|
+
};
|
|
118
138
|
});
|
|
119
139
|
</script>
|
|
120
140
|
|
|
@@ -138,7 +138,7 @@ export interface Props extends Omit<HTMLButtonAttributes, "children"> {
|
|
|
138
138
|
}
|
|
139
139
|
export declare const DROPDOWN_MENU_BASE_CLASSES = "stuic-dropdown-menu relative inline-block";
|
|
140
140
|
export declare const DROPDOWN_MENU_TRIGGER_CLASSES = "\n\t\tinline-flex items-center justify-center gap-2\n\t\tpx-3 py-2\n\t\trounded-md border\n\t\tbg-white dark:bg-neutral-800\n\t\ttext-neutral-900 dark:text-neutral-100\n\t\tborder-neutral-200 dark:border-neutral-700\n\t\thover:brightness-95 dark:hover:brightness-110\n\t\tfocus-visible:outline-2 focus-visible:outline-offset-2\n\t\tcursor-pointer\n\t";
|
|
141
|
-
export declare const DROPDOWN_MENU_DROPDOWN_CLASSES = "\n\t\tstuic-dropdown-menu-dropdown\n\t\tbg-white dark:bg-neutral-800\n\t\ttext-neutral-900 dark:text-neutral-100\n\t\tborder border-neutral-200 dark:border-neutral-700\n\t\trounded-md shadow-
|
|
141
|
+
export declare const DROPDOWN_MENU_DROPDOWN_CLASSES = "\n\t\tstuic-dropdown-menu-dropdown\n\t\tbg-white dark:bg-neutral-800\n\t\ttext-neutral-900 dark:text-neutral-100\n\t\tborder border-neutral-200 dark:border-neutral-700\n\t\trounded-md shadow-sm\n\t\tp-1\n\t\toverflow-y-auto\n\t\tz-50\n\t\tmin-w-48\n\t";
|
|
142
142
|
export declare const DROPDOWN_MENU_ITEM_CLASSES = "\n\t\tw-full\n\t\tflex items-center gap-2\n\t\tpx-2 py-1.5\n\t\tmin-h-[44px]\n\t\ttext-left text-sm\n\t\trounded-sm\n\t\tcursor-pointer\n\t\ttouch-action-manipulation\n\t\thover:bg-neutral-100 dark:hover:bg-neutral-700\n\t\tfocus:outline-none\n\t\tfocus-visible:bg-neutral-200 dark:focus-visible:bg-neutral-600\n\t";
|
|
143
143
|
export declare const DROPDOWN_MENU_DIVIDER_CLASSES = "\n\t\th-px my-1\n\t\tbg-neutral-200 dark:bg-neutral-700\n\t";
|
|
144
144
|
export declare const DROPDOWN_MENU_HEADER_CLASSES = "\n\t\tpx-2 py-1.5\n\t\ttext-xs font-semibold uppercase tracking-wide\n\t\ttext-neutral-500 dark:text-neutral-400\n\t\tselect-none\n\t";
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * from "./file-from-bloburl.js";
|
|
|
11
11
|
export * from "./force-download.js";
|
|
12
12
|
export * from "./get-file-type-label.js";
|
|
13
13
|
export * from "./get-id.js";
|
|
14
|
+
export * from "./input-history.svelte.js";
|
|
14
15
|
export * from "./is-browser.js";
|
|
15
16
|
export * from "./is-image.js";
|
|
16
17
|
export * from "./is-mac.js";
|
package/dist/utils/index.js
CHANGED
|
@@ -11,6 +11,7 @@ export * from "./file-from-bloburl.js";
|
|
|
11
11
|
export * from "./force-download.js";
|
|
12
12
|
export * from "./get-file-type-label.js";
|
|
13
13
|
export * from "./get-id.js";
|
|
14
|
+
export * from "./input-history.svelte.js";
|
|
14
15
|
export * from "./is-browser.js";
|
|
15
16
|
export * from "./is-image.js";
|
|
16
17
|
export * from "./is-mac.js";
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for InputHistory
|
|
3
|
+
*/
|
|
4
|
+
export interface InputHistoryOptions {
|
|
5
|
+
/** Composite key parts for namespacing (e.g., [projectId, domain, entity, type]) */
|
|
6
|
+
keyParts: string[];
|
|
7
|
+
/** Maximum number of entries to store (default: 10) */
|
|
8
|
+
maxEntries?: number;
|
|
9
|
+
/** App ID prefix for the storage key (default: "app") */
|
|
10
|
+
appId?: string;
|
|
11
|
+
/** Feature name for the key (default: "input-history") */
|
|
12
|
+
featureName?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A reactive input history manager with localStorage persistence and arrow key navigation.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const history = new InputHistory({
|
|
20
|
+
* keyParts: [projectId, domain, entity, type],
|
|
21
|
+
* appId: "joy",
|
|
22
|
+
* featureName: "filter-history"
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // Add entry on submit
|
|
26
|
+
* history.add(query);
|
|
27
|
+
*
|
|
28
|
+
* // Navigate with arrow keys
|
|
29
|
+
* history.navigateUp(); // Go to older entry
|
|
30
|
+
* history.navigateDown(); // Go to newer entry
|
|
31
|
+
*
|
|
32
|
+
* // Get current entry for display
|
|
33
|
+
* const current = history.getCurrent();
|
|
34
|
+
*
|
|
35
|
+
* // Reset navigation when user starts typing
|
|
36
|
+
* history.reset();
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare class InputHistory {
|
|
40
|
+
#private;
|
|
41
|
+
/** Storage key for this history instance */
|
|
42
|
+
readonly key: string;
|
|
43
|
+
/** Maximum entries to store */
|
|
44
|
+
readonly maxEntries: number;
|
|
45
|
+
constructor(options: InputHistoryOptions);
|
|
46
|
+
/** Get the stored history entries (newest first) */
|
|
47
|
+
get entries(): string[];
|
|
48
|
+
/** Get current navigation index (-1 when not navigating) */
|
|
49
|
+
get navigationIndex(): number;
|
|
50
|
+
/** Check if currently navigating through history */
|
|
51
|
+
get isNavigating(): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Add a new query to history (called on Enter/submit).
|
|
54
|
+
* Deduplicates and limits to maxEntries.
|
|
55
|
+
*/
|
|
56
|
+
add(query: string): void;
|
|
57
|
+
/**
|
|
58
|
+
* Navigate up (to older entries).
|
|
59
|
+
* On first call, saves current input value.
|
|
60
|
+
* @param currentValue - The current input value (saved on first navigation)
|
|
61
|
+
*/
|
|
62
|
+
navigateUp(currentValue?: string): string | null;
|
|
63
|
+
/**
|
|
64
|
+
* Navigate down (to newer entries).
|
|
65
|
+
* When reaching past newest, returns to temp value.
|
|
66
|
+
*/
|
|
67
|
+
navigateDown(): string | null;
|
|
68
|
+
/**
|
|
69
|
+
* Get the current history entry based on navigation index.
|
|
70
|
+
* Returns null if not navigating.
|
|
71
|
+
*/
|
|
72
|
+
getCurrent(): string | null;
|
|
73
|
+
/**
|
|
74
|
+
* Reset navigation state (call when user starts typing).
|
|
75
|
+
*/
|
|
76
|
+
reset(): void;
|
|
77
|
+
/**
|
|
78
|
+
* Clear all history for this key.
|
|
79
|
+
*/
|
|
80
|
+
clear(): void;
|
|
81
|
+
/**
|
|
82
|
+
* Clear all histories matching a pattern prefix.
|
|
83
|
+
* Call this on logout to clean up user data.
|
|
84
|
+
*
|
|
85
|
+
* @param pattern - Key prefix to match (e.g., "joy:input-history")
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* // On logout, clear all input histories
|
|
90
|
+
* InputHistory.clearAllMatching("joy:input-history");
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
static clearAllMatching(pattern: string): void;
|
|
94
|
+
/**
|
|
95
|
+
* Clear all registered histories (nuclear option).
|
|
96
|
+
*/
|
|
97
|
+
static clearAll(): void;
|
|
98
|
+
/**
|
|
99
|
+
* Get all registered history keys (for debugging).
|
|
100
|
+
*/
|
|
101
|
+
static getRegisteredKeys(): string[];
|
|
102
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { localStorageState } from "./persistent-state.svelte.js";
|
|
2
|
+
/**
|
|
3
|
+
* A reactive input history manager with localStorage persistence and arrow key navigation.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* const history = new InputHistory({
|
|
8
|
+
* keyParts: [projectId, domain, entity, type],
|
|
9
|
+
* appId: "joy",
|
|
10
|
+
* featureName: "filter-history"
|
|
11
|
+
* });
|
|
12
|
+
*
|
|
13
|
+
* // Add entry on submit
|
|
14
|
+
* history.add(query);
|
|
15
|
+
*
|
|
16
|
+
* // Navigate with arrow keys
|
|
17
|
+
* history.navigateUp(); // Go to older entry
|
|
18
|
+
* history.navigateDown(); // Go to newer entry
|
|
19
|
+
*
|
|
20
|
+
* // Get current entry for display
|
|
21
|
+
* const current = history.getCurrent();
|
|
22
|
+
*
|
|
23
|
+
* // Reset navigation when user starts typing
|
|
24
|
+
* history.reset();
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export class InputHistory {
|
|
28
|
+
/** Storage key for this history instance */
|
|
29
|
+
key;
|
|
30
|
+
/** Maximum entries to store */
|
|
31
|
+
maxEntries;
|
|
32
|
+
/** Persistent state for stored history entries */
|
|
33
|
+
#storage;
|
|
34
|
+
/** Current navigation index (-1 means "not navigating", 0 is newest, length-1 is oldest) */
|
|
35
|
+
#navigationIndex = $state(-1);
|
|
36
|
+
/** Temporary value holder for current input before navigation started */
|
|
37
|
+
#tempValue = $state("");
|
|
38
|
+
constructor(options) {
|
|
39
|
+
const { keyParts, maxEntries = 10, appId = "app", featureName = "input-history", } = options;
|
|
40
|
+
this.maxEntries = maxEntries;
|
|
41
|
+
// Build composite key: "joy:input-history:projectId:domain:entity:type"
|
|
42
|
+
this.key = [appId, featureName, ...keyParts].filter(Boolean).join(":");
|
|
43
|
+
// Initialize persistent storage
|
|
44
|
+
this.#storage = localStorageState(this.key, []);
|
|
45
|
+
// Register this instance for cleanup
|
|
46
|
+
InputHistory.#register(this.key);
|
|
47
|
+
}
|
|
48
|
+
// ─────────────────────────────────────────────────────────────
|
|
49
|
+
// Public API
|
|
50
|
+
// ─────────────────────────────────────────────────────────────
|
|
51
|
+
/** Get the stored history entries (newest first) */
|
|
52
|
+
get entries() {
|
|
53
|
+
return this.#storage.current;
|
|
54
|
+
}
|
|
55
|
+
/** Get current navigation index (-1 when not navigating) */
|
|
56
|
+
get navigationIndex() {
|
|
57
|
+
return this.#navigationIndex;
|
|
58
|
+
}
|
|
59
|
+
/** Check if currently navigating through history */
|
|
60
|
+
get isNavigating() {
|
|
61
|
+
return this.#navigationIndex >= 0;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Add a new query to history (called on Enter/submit).
|
|
65
|
+
* Deduplicates and limits to maxEntries.
|
|
66
|
+
*/
|
|
67
|
+
add(query) {
|
|
68
|
+
query = query.trim();
|
|
69
|
+
if (!query)
|
|
70
|
+
return;
|
|
71
|
+
const current = [...this.#storage.current];
|
|
72
|
+
// Remove duplicates (case-sensitive)
|
|
73
|
+
const filtered = current.filter((item) => item !== query);
|
|
74
|
+
// Add to beginning (newest first)
|
|
75
|
+
filtered.unshift(query);
|
|
76
|
+
// Limit to maxEntries
|
|
77
|
+
this.#storage.current = filtered.slice(0, this.maxEntries);
|
|
78
|
+
// Reset navigation after adding
|
|
79
|
+
this.reset();
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Navigate up (to older entries).
|
|
83
|
+
* On first call, saves current input value.
|
|
84
|
+
* @param currentValue - The current input value (saved on first navigation)
|
|
85
|
+
*/
|
|
86
|
+
navigateUp(currentValue) {
|
|
87
|
+
const entries = this.entries;
|
|
88
|
+
if (entries.length === 0)
|
|
89
|
+
return null;
|
|
90
|
+
// If not navigating yet, save current value and start
|
|
91
|
+
if (this.#navigationIndex < 0) {
|
|
92
|
+
this.#tempValue = currentValue ?? "";
|
|
93
|
+
this.#navigationIndex = 0;
|
|
94
|
+
}
|
|
95
|
+
else if (this.#navigationIndex < entries.length - 1) {
|
|
96
|
+
// Move to older entry
|
|
97
|
+
this.#navigationIndex++;
|
|
98
|
+
}
|
|
99
|
+
// At oldest entry, stay there
|
|
100
|
+
return this.getCurrent();
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Navigate down (to newer entries).
|
|
104
|
+
* When reaching past newest, returns to temp value.
|
|
105
|
+
*/
|
|
106
|
+
navigateDown() {
|
|
107
|
+
if (this.#navigationIndex < 0)
|
|
108
|
+
return null;
|
|
109
|
+
if (this.#navigationIndex > 0) {
|
|
110
|
+
// Move to newer entry
|
|
111
|
+
this.#navigationIndex--;
|
|
112
|
+
return this.getCurrent();
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
// At newest entry, go back to temp value
|
|
116
|
+
const temp = this.#tempValue;
|
|
117
|
+
this.reset();
|
|
118
|
+
return temp;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get the current history entry based on navigation index.
|
|
123
|
+
* Returns null if not navigating.
|
|
124
|
+
*/
|
|
125
|
+
getCurrent() {
|
|
126
|
+
if (this.#navigationIndex < 0)
|
|
127
|
+
return null;
|
|
128
|
+
return this.entries[this.#navigationIndex] ?? null;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Reset navigation state (call when user starts typing).
|
|
132
|
+
*/
|
|
133
|
+
reset() {
|
|
134
|
+
this.#navigationIndex = -1;
|
|
135
|
+
this.#tempValue = "";
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Clear all history for this key.
|
|
139
|
+
*/
|
|
140
|
+
clear() {
|
|
141
|
+
this.#storage.current = [];
|
|
142
|
+
this.reset();
|
|
143
|
+
}
|
|
144
|
+
// ─────────────────────────────────────────────────────────────
|
|
145
|
+
// Static cleanup registration
|
|
146
|
+
// ─────────────────────────────────────────────────────────────
|
|
147
|
+
/** Registry of all history keys (for cleanup on logout) */
|
|
148
|
+
static #registeredKeys = new Set();
|
|
149
|
+
/** Register a key for potential cleanup */
|
|
150
|
+
static #register(key) {
|
|
151
|
+
InputHistory.#registeredKeys.add(key);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Clear all histories matching a pattern prefix.
|
|
155
|
+
* Call this on logout to clean up user data.
|
|
156
|
+
*
|
|
157
|
+
* @param pattern - Key prefix to match (e.g., "joy:input-history")
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```ts
|
|
161
|
+
* // On logout, clear all input histories
|
|
162
|
+
* InputHistory.clearAllMatching("joy:input-history");
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
static clearAllMatching(pattern) {
|
|
166
|
+
// Clear from our registry
|
|
167
|
+
for (const key of InputHistory.#registeredKeys) {
|
|
168
|
+
if (key.startsWith(pattern)) {
|
|
169
|
+
localStorage.removeItem(key);
|
|
170
|
+
InputHistory.#registeredKeys.delete(key);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Also scan localStorage for any keys we might have missed
|
|
174
|
+
// (e.g., from previous sessions)
|
|
175
|
+
for (let i = localStorage.length - 1; i >= 0; i--) {
|
|
176
|
+
const key = localStorage.key(i);
|
|
177
|
+
if (key?.startsWith(pattern)) {
|
|
178
|
+
localStorage.removeItem(key);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Clear all registered histories (nuclear option).
|
|
184
|
+
*/
|
|
185
|
+
static clearAll() {
|
|
186
|
+
for (const key of InputHistory.#registeredKeys) {
|
|
187
|
+
localStorage.removeItem(key);
|
|
188
|
+
}
|
|
189
|
+
InputHistory.#registeredKeys.clear();
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Get all registered history keys (for debugging).
|
|
193
|
+
*/
|
|
194
|
+
static getRegisteredKeys() {
|
|
195
|
+
return [...InputHistory.#registeredKeys];
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -28,7 +28,7 @@ export function observeExists(selector, options = {}) {
|
|
|
28
28
|
throw new TypeError("Expecting non empty selector");
|
|
29
29
|
const { rootElement = document.body } = options;
|
|
30
30
|
const ns = `[observeExists] [${selector}]`;
|
|
31
|
-
const
|
|
31
|
+
const clogDebug = (...args) => console.debug(ns, ...args);
|
|
32
32
|
const check = () => rootElement.querySelector(selector) !== null;
|
|
33
33
|
let current = $state(check());
|
|
34
34
|
//
|
|
@@ -47,14 +47,14 @@ export function observeExists(selector, options = {}) {
|
|
|
47
47
|
current = check();
|
|
48
48
|
});
|
|
49
49
|
// start observing now
|
|
50
|
-
|
|
50
|
+
clogDebug(`connecting...`);
|
|
51
51
|
observer.observe(rootElement, { childList: true, subtree: true });
|
|
52
52
|
return {
|
|
53
53
|
get current() {
|
|
54
54
|
return current;
|
|
55
55
|
},
|
|
56
56
|
disconnect() {
|
|
57
|
-
|
|
57
|
+
clogDebug(`disconnecting...`);
|
|
58
58
|
observer.disconnect();
|
|
59
59
|
},
|
|
60
60
|
forceCheck() {
|