@ibgib/web-gib 0.0.45 → 0.0.47
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/CHANGELOG.md +5 -0
- package/README.md +25 -30
- package/dist/AUTO-GENERATED-version.d.mts +1 -1
- package/dist/AUTO-GENERATED-version.mjs +1 -1
- package/dist/helpers.d.mts.map +1 -1
- package/dist/helpers.mjs +28 -27
- package/dist/helpers.mjs.map +1 -1
- package/dist/ui/component/ibgib-dynamic-component-bases.d.mts +54 -1
- package/dist/ui/component/ibgib-dynamic-component-bases.d.mts.map +1 -1
- package/dist/ui/component/ibgib-dynamic-component-bases.mjs +176 -2
- package/dist/ui/component/ibgib-dynamic-component-bases.mjs.map +1 -1
- package/dist/ui/ui-constants.d.mts +5 -0
- package/dist/ui/ui-constants.d.mts.map +1 -1
- package/dist/ui/ui-constants.mjs +7 -0
- package/dist/ui/ui-constants.mjs.map +1 -1
- package/dist/ui/ui-helpers.d.mts +17 -1
- package/dist/ui/ui-helpers.d.mts.map +1 -1
- package/dist/ui/ui-helpers.mjs +94 -22
- package/dist/ui/ui-helpers.mjs.map +1 -1
- package/package.json +2 -2
- package/src/AUTO-GENERATED-version.mts +1 -1
- package/src/helpers.mts +34 -25
- package/src/ui/component/ARCHITECTURE.md +180 -0
- package/src/ui/component/README.md +201 -0
- package/src/ui/component/ibgib-dynamic-component-bases.mts +225 -2
- package/src/ui/ui-constants.mts +10 -0
- package/src/ui/ui-helpers.mts +137 -23
- package/tools/auto-generated-agent-skills/skills/ibgib-create-component/SKILL.md +9 -5
package/src/ui/ui-helpers.mts
CHANGED
|
@@ -17,9 +17,14 @@ const logalot = GLOBAL_LOG_A_LOT;
|
|
|
17
17
|
* @param {boolean} [details.prompt] - If true, shows a text input for prompting the user.
|
|
18
18
|
* @returns {Promise<string | null>} - A promise that resolves with the text entered by the user (if prompt is true) or null if canceled/dismissed.
|
|
19
19
|
*/
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
export interface FullscreenDialogController {
|
|
21
|
+
close: () => void;
|
|
22
|
+
update: (opts: { title?: string; msg?: string; animationEmoji?: string }) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ShowFullscreenDialogOpts {
|
|
26
|
+
title: string;
|
|
27
|
+
msg: string;
|
|
23
28
|
/**
|
|
24
29
|
* determines if to show the cancel button. does NOT control whether or not
|
|
25
30
|
* we show the input control.
|
|
@@ -30,19 +35,36 @@ export async function showFullscreenDialog(opts: {
|
|
|
30
35
|
*
|
|
31
36
|
* @see {@link showInput}
|
|
32
37
|
*/
|
|
33
|
-
prompt?: boolean
|
|
38
|
+
prompt?: boolean;
|
|
34
39
|
/**
|
|
35
40
|
* this flag determines if the user input field is shown. If this is truthy,
|
|
36
41
|
* then {@link prompt} will be ignored and always set to `true`.
|
|
37
42
|
*
|
|
38
43
|
* @see {@link prompt}
|
|
39
44
|
*/
|
|
40
|
-
showInput?: boolean
|
|
41
|
-
defaultValue?: string
|
|
42
|
-
okButtonTitle?: string
|
|
43
|
-
cancelButtonTitle?: string
|
|
44
|
-
isPassword?: boolean
|
|
45
|
-
|
|
45
|
+
showInput?: boolean;
|
|
46
|
+
defaultValue?: string;
|
|
47
|
+
okButtonTitle?: string;
|
|
48
|
+
cancelButtonTitle?: string;
|
|
49
|
+
isPassword?: boolean;
|
|
50
|
+
isBusy?: boolean;
|
|
51
|
+
animationEmoji?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function showFullscreenDialog(opts: ShowFullscreenDialogOpts & { isBusy: true }): Promise<FullscreenDialogController>;
|
|
55
|
+
export async function showFullscreenDialog(opts: ShowFullscreenDialogOpts & { isBusy?: false }): Promise<string | undefined>;
|
|
56
|
+
export async function showFullscreenDialog({
|
|
57
|
+
title,
|
|
58
|
+
msg,
|
|
59
|
+
prompt,
|
|
60
|
+
showInput,
|
|
61
|
+
defaultValue,
|
|
62
|
+
okButtonTitle,
|
|
63
|
+
cancelButtonTitle,
|
|
64
|
+
isPassword,
|
|
65
|
+
isBusy,
|
|
66
|
+
animationEmoji,
|
|
67
|
+
}: ShowFullscreenDialogOpts): Promise<string | undefined | FullscreenDialogController> {
|
|
46
68
|
const lc = `[${showFullscreenDialog.name}]`;
|
|
47
69
|
try {
|
|
48
70
|
if (logalot) { console.log(`${lc} starting... (I: 037b228a2568181cd3eb25dba4e03225)`); }
|
|
@@ -67,10 +89,10 @@ export async function showFullscreenDialog(opts: {
|
|
|
67
89
|
throw new Error(`(UNEXPECTED) One or more dialog elements not found in DOM (E: 578e37c0271cc57a99e95e780ad18a25)`);
|
|
68
90
|
}
|
|
69
91
|
|
|
70
|
-
titleElement.textContent =
|
|
92
|
+
titleElement.textContent = title || '';
|
|
71
93
|
[...messageElement.childNodes as any].forEach(child => messageElement.removeChild(child));
|
|
72
94
|
// messageElement.innerHTML = '';
|
|
73
|
-
let msgLines = (
|
|
95
|
+
let msgLines = (msg || '').split('\n');
|
|
74
96
|
msgLines.forEach(line => {
|
|
75
97
|
const pLine = document.createElement('p');
|
|
76
98
|
pLine.textContent = line;
|
|
@@ -78,11 +100,24 @@ export async function showFullscreenDialog(opts: {
|
|
|
78
100
|
});
|
|
79
101
|
// messageElement.textContent = opts.msg || '';
|
|
80
102
|
|
|
81
|
-
//
|
|
103
|
+
// Add dynamic animation emoji if provided
|
|
104
|
+
if (animationEmoji) {
|
|
105
|
+
const animDiv = document.createElement('div');
|
|
106
|
+
animDiv.id = 'fullscreen-dialog-animation-emoji';
|
|
107
|
+
animDiv.textContent = animationEmoji;
|
|
108
|
+
animDiv.style.fontSize = '3.5rem';
|
|
109
|
+
animDiv.style.textAlign = 'center';
|
|
110
|
+
animDiv.style.margin = '1.5rem 0';
|
|
111
|
+
animDiv.style.animation = 'spin-bounce 5s infinite ease-in-out';
|
|
112
|
+
messageElement.appendChild(animDiv);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// reset the input classList and button displays
|
|
82
116
|
promptInput.classList.remove('collapsed');
|
|
83
117
|
cancelButton.classList.remove('collapsed');
|
|
84
118
|
cancelButton.style.display = 'inline-block';
|
|
85
|
-
okButton.
|
|
119
|
+
okButton.style.display = 'inline-block';
|
|
120
|
+
okButton.textContent = okButtonTitle || 'OK';
|
|
86
121
|
|
|
87
122
|
// Prompt-specific setup
|
|
88
123
|
const onKeypress = (ev: KeyboardEvent) => {
|
|
@@ -93,15 +128,19 @@ export async function showFullscreenDialog(opts: {
|
|
|
93
128
|
if (isEnter && ev.ctrlKey) { okButton.click(); }
|
|
94
129
|
};
|
|
95
130
|
|
|
96
|
-
if (
|
|
131
|
+
if (isBusy) {
|
|
132
|
+
promptInput.classList.add('collapsed');
|
|
133
|
+
cancelButton.style.display = 'none';
|
|
134
|
+
okButton.style.display = 'none';
|
|
135
|
+
} else if (prompt || showInput) {
|
|
97
136
|
promptInput.addEventListener('keypress', onKeypress);
|
|
98
|
-
if (
|
|
137
|
+
if (showInput) {
|
|
99
138
|
promptInput.autofocus = true;
|
|
100
|
-
promptInput.value =
|
|
139
|
+
promptInput.value = defaultValue ?? '';
|
|
101
140
|
promptInput.addEventListener('keypress', (ev) => {
|
|
102
141
|
if (ev.key === 'Enter') { okButton.click(); }
|
|
103
142
|
});
|
|
104
|
-
if (
|
|
143
|
+
if (isPassword) {
|
|
105
144
|
promptInput.type = 'password';
|
|
106
145
|
promptInput.autocomplete = 'off';
|
|
107
146
|
promptInput.required = true;
|
|
@@ -115,18 +154,91 @@ export async function showFullscreenDialog(opts: {
|
|
|
115
154
|
promptInput.autofocus = false;
|
|
116
155
|
promptInput.classList.add('collapsed');
|
|
117
156
|
}
|
|
118
|
-
cancelButton.textContent =
|
|
157
|
+
cancelButton.textContent = cancelButtonTitle || 'Cancel';
|
|
119
158
|
} else {
|
|
120
159
|
promptInput.classList.add('collapsed'); // Hide prompt input
|
|
121
160
|
cancelButton.style.display = 'none'; // Hide cancel button for alerts
|
|
122
161
|
}
|
|
123
162
|
|
|
124
|
-
return new Promise<
|
|
163
|
+
return new Promise<any>((resolve) => {
|
|
164
|
+
if (isBusy) {
|
|
165
|
+
const onCancelEvent = (ev: Event) => {
|
|
166
|
+
ev.preventDefault();
|
|
167
|
+
};
|
|
168
|
+
dialog.addEventListener('cancel', onCancelEvent);
|
|
169
|
+
|
|
170
|
+
const close = () => {
|
|
171
|
+
dialog.removeEventListener('cancel', onCancelEvent);
|
|
172
|
+
dialog.close();
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const update = ({
|
|
176
|
+
title: newTitle,
|
|
177
|
+
msg: newMsg,
|
|
178
|
+
animationEmoji: newEmoji,
|
|
179
|
+
}: {
|
|
180
|
+
title?: string;
|
|
181
|
+
msg?: string;
|
|
182
|
+
animationEmoji?: string;
|
|
183
|
+
}) => {
|
|
184
|
+
if (newTitle !== undefined) {
|
|
185
|
+
titleElement.textContent = newTitle;
|
|
186
|
+
}
|
|
187
|
+
if (newMsg !== undefined) {
|
|
188
|
+
// Clear non-emoji nodes
|
|
189
|
+
[...messageElement.childNodes as any].forEach(child => {
|
|
190
|
+
if (child.id !== 'fullscreen-dialog-animation-emoji') {
|
|
191
|
+
messageElement.removeChild(child);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
// Insert new lines before the animation emoji
|
|
195
|
+
let newMsgLines = newMsg.split('\n');
|
|
196
|
+
const animDiv = document.getElementById('fullscreen-dialog-animation-emoji');
|
|
197
|
+
newMsgLines.forEach(line => {
|
|
198
|
+
const pLine = document.createElement('p');
|
|
199
|
+
pLine.textContent = line;
|
|
200
|
+
if (animDiv) {
|
|
201
|
+
messageElement.insertBefore(pLine, animDiv);
|
|
202
|
+
} else {
|
|
203
|
+
messageElement.appendChild(pLine);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
if (newEmoji !== undefined) {
|
|
208
|
+
let animDiv = document.getElementById('fullscreen-dialog-animation-emoji');
|
|
209
|
+
if (newEmoji) {
|
|
210
|
+
if (!animDiv) {
|
|
211
|
+
animDiv = document.createElement('div');
|
|
212
|
+
animDiv.id = 'fullscreen-dialog-animation-emoji';
|
|
213
|
+
animDiv.style.fontSize = '3.5rem';
|
|
214
|
+
animDiv.style.textAlign = 'center';
|
|
215
|
+
animDiv.style.margin = '1.5rem 0';
|
|
216
|
+
animDiv.style.animation = 'spin-bounce 5s infinite ease-in-out';
|
|
217
|
+
messageElement.appendChild(animDiv);
|
|
218
|
+
}
|
|
219
|
+
animDiv.textContent = newEmoji;
|
|
220
|
+
} else if (animDiv) {
|
|
221
|
+
animDiv.remove();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
if (!dialog.open) {
|
|
227
|
+
dialog.showModal();
|
|
228
|
+
}
|
|
229
|
+
delay(1000).then(() => {
|
|
230
|
+
dialogBody.scrollTo({ top: 0, behavior: 'smooth' });
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
resolve({ close, update });
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
125
237
|
// handlers
|
|
126
238
|
|
|
127
239
|
/** helper used in other handlers */
|
|
128
240
|
const removeEventListeners = () => {
|
|
129
|
-
if (
|
|
241
|
+
if (prompt || showInput) {
|
|
130
242
|
promptInput.removeEventListener('keypress', onKeypress);
|
|
131
243
|
}
|
|
132
244
|
okButton.removeEventListener('click', onOK);
|
|
@@ -136,7 +248,7 @@ export async function showFullscreenDialog(opts: {
|
|
|
136
248
|
/** user clicks OK or hits "ENTER" in input */
|
|
137
249
|
const onOK = () => {
|
|
138
250
|
removeEventListeners();
|
|
139
|
-
let result =
|
|
251
|
+
let result = showInput ?
|
|
140
252
|
promptInput.value ?? '' :
|
|
141
253
|
true.toString();
|
|
142
254
|
if (promptInput) { promptInput.value = ''; } // Clear input for next time
|
|
@@ -170,7 +282,9 @@ export async function showFullscreenDialog(opts: {
|
|
|
170
282
|
dialog.addEventListener('close', onClose); // e.g. escape key
|
|
171
283
|
|
|
172
284
|
// show it
|
|
173
|
-
dialog.
|
|
285
|
+
if (!dialog.open) {
|
|
286
|
+
dialog.showModal();
|
|
287
|
+
}
|
|
174
288
|
delay(1000).then(() => {
|
|
175
289
|
dialogBody.scrollTo({ top: 0, behavior: 'smooth' });
|
|
176
290
|
});
|
|
@@ -7,6 +7,8 @@ description: Scaffolds a new reusable ibgib UI component. Generates the componen
|
|
|
7
7
|
|
|
8
8
|
This skill scaffolds a new UI component for an ibgib application. components are implemented as Web Components (Custom Elements) using the `ibgib-web-gib` framework, which provides deep integration with ibgib timelines and real-time updates.
|
|
9
9
|
|
|
10
|
+
For more documentation, refer to the ibgib component [README.md](libs\web-gib\src\ui\component\README.md)
|
|
11
|
+
|
|
10
12
|
## Goal
|
|
11
13
|
To create a standard, reactive UI component that can be registered with the `ibgib-component-service` and rendered within an app shell.
|
|
12
14
|
|
|
@@ -20,14 +22,16 @@ Wait for the user to provide or confirm these tokens:
|
|
|
20
22
|
|
|
21
23
|
## Output Files
|
|
22
24
|
The skill will generate:
|
|
23
|
-
1. `src/components/{{COMPONENT_NAME}}/
|
|
24
|
-
2. `src/components/{{COMPONENT_NAME}}/{{COMPONENT_NAME}}.
|
|
25
|
-
3. `src/components/{{COMPONENT_NAME}}/{{COMPONENT_NAME}}.
|
|
25
|
+
1. `src/components/{{COMPONENT_NAME}}/IMPLEMENTATION.md` (Design spec, requirements, and checklist)
|
|
26
|
+
2. `src/components/{{COMPONENT_NAME}}/{{COMPONENT_NAME}}.mts`
|
|
27
|
+
3. `src/components/{{COMPONENT_NAME}}/{{COMPONENT_NAME}}.html`
|
|
28
|
+
4. `src/components/{{COMPONENT_NAME}}/{{COMPONENT_NAME}}.css`
|
|
26
29
|
|
|
27
30
|
## Usage Instructions
|
|
28
31
|
1. Ensure the `src/components/{{COMPONENT_NAME}}` directory exists.
|
|
29
|
-
2.
|
|
30
|
-
3.
|
|
32
|
+
2. Before scaffolding the code, create the `IMPLEMENTATION.md` file detailing the core layout, settings, requirements, and an implementation checklist.
|
|
33
|
+
3. Generate the implementation files (.mts, .html, .css) from templates matching the specifications in the implementation plan.
|
|
34
|
+
4. Import and register the `{{COMPONENT_CLASS_PREFIX}}ComponentMeta` in your `ShellService`'s `registerComponents()` method.
|
|
31
35
|
|
|
32
36
|
## Component Lifecycle
|
|
33
37
|
- `initialize(opts)`: Called when the component is first given an ibGib address.
|