@eclipse-docks/core 0.7.108 → 0.7.109
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/api/index.js +3 -3
- package/dist/api/services.d.ts +1 -1
- package/dist/api/services.d.ts.map +1 -1
- package/dist/api/types.d.ts +1 -1
- package/dist/api/types.d.ts.map +1 -1
- package/dist/{api-BilEZ2UQ.js → api-CuHseuRW.js} +2 -2
- package/dist/{api-BilEZ2UQ.js.map → api-CuHseuRW.js.map} +1 -1
- package/dist/core/dialogservice.d.ts.map +1 -1
- package/dist/dialogs/form-dialog.d.ts +46 -0
- package/dist/dialogs/form-dialog.d.ts.map +1 -0
- package/dist/dialogs/index.d.ts +1 -0
- package/dist/dialogs/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/parts/index.js +1 -1
- package/dist/{parts-DECzUKtn.js → parts-D__47G7C.js} +164 -3
- package/dist/parts-D__47G7C.js.map +1 -0
- package/package.json +1 -1
- package/src/api/services.ts +7 -1
- package/src/api/types.ts +4 -1
- package/src/core/dialogservice.ts +6 -1
- package/src/dialogs/form-dialog.ts +239 -0
- package/src/dialogs/index.ts +8 -0
- package/dist/parts-DECzUKtn.js.map +0 -1
package/package.json
CHANGED
package/src/api/services.ts
CHANGED
|
@@ -35,8 +35,14 @@ export {
|
|
|
35
35
|
navigableInfoDialog,
|
|
36
36
|
confirmDialog,
|
|
37
37
|
promptDialog,
|
|
38
|
+
formDialog,
|
|
39
|
+
formDialogRequired,
|
|
40
|
+
validateFormDialogFields,
|
|
38
41
|
filebrowserDialog,
|
|
39
|
-
type NavigableDialogAction
|
|
42
|
+
type NavigableDialogAction,
|
|
43
|
+
type FormDialogField,
|
|
44
|
+
type FormDialogFieldType,
|
|
45
|
+
type FormDialogOptions,
|
|
40
46
|
} from '../dialogs';
|
|
41
47
|
export {
|
|
42
48
|
toastInfo,
|
package/src/api/types.ts
CHANGED
|
@@ -36,7 +36,10 @@ export type {
|
|
|
36
36
|
export type {
|
|
37
37
|
NavigableDialogAction,
|
|
38
38
|
FilebrowserDialogMode,
|
|
39
|
-
FilebrowserDialogState
|
|
39
|
+
FilebrowserDialogState,
|
|
40
|
+
FormDialogField,
|
|
41
|
+
FormDialogFieldType,
|
|
42
|
+
FormDialogOptions,
|
|
40
43
|
} from '../dialogs';
|
|
41
44
|
export type {
|
|
42
45
|
TreeNode,
|
|
@@ -182,8 +182,13 @@ class DialogService {
|
|
|
182
182
|
}
|
|
183
183
|
const stateWithClose = { ...state, close: cleanup };
|
|
184
184
|
|
|
185
|
+
const dialogLabel =
|
|
186
|
+
state && typeof state === 'object' && typeof (state as { label?: unknown }).label === 'string'
|
|
187
|
+
? (state as { label: string }).label
|
|
188
|
+
: (contribution.label || dialogId);
|
|
189
|
+
|
|
185
190
|
const template = html`
|
|
186
|
-
<wa-dialog label="${
|
|
191
|
+
<wa-dialog label="${dialogLabel}" open @wa-request-close=${cleanup}>
|
|
187
192
|
<style>
|
|
188
193
|
.dialog-service-content {
|
|
189
194
|
display: flex;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { html, css, PropertyValues } from "lit";
|
|
2
|
+
import { customElement, state, property } from "lit/decorators.js";
|
|
3
|
+
import { DocksDialogContent } from "../parts/dialog-content";
|
|
4
|
+
import { contributionRegistry } from "../core/contributionregistry";
|
|
5
|
+
import {
|
|
6
|
+
DIALOG_CONTRIBUTION_TARGET,
|
|
7
|
+
OK_BUTTON,
|
|
8
|
+
CANCEL_BUTTON,
|
|
9
|
+
dialogService,
|
|
10
|
+
} from "../core/dialogservice";
|
|
11
|
+
|
|
12
|
+
/** HTML input types supported by {@link formDialog}. */
|
|
13
|
+
export type FormDialogFieldType = "text" | "password" | "url" | "email" | "number";
|
|
14
|
+
|
|
15
|
+
export interface FormDialogField {
|
|
16
|
+
/** Key in the returned values record. */
|
|
17
|
+
name: string;
|
|
18
|
+
label: string;
|
|
19
|
+
type?: FormDialogFieldType;
|
|
20
|
+
value?: string;
|
|
21
|
+
placeholder?: string;
|
|
22
|
+
/** When false, empty values are allowed. Defaults to true. */
|
|
23
|
+
required?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface FormDialogOptions {
|
|
27
|
+
/** Dialog window title (shown on `wa-dialog`). */
|
|
28
|
+
label: string;
|
|
29
|
+
fields: FormDialogField[];
|
|
30
|
+
/** Optional message above the fields (plain text or markdown). */
|
|
31
|
+
message?: string;
|
|
32
|
+
markdown?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface FormDialogState extends FormDialogOptions {
|
|
36
|
+
resolve: (values: Record<string, string> | null) => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@customElement("docks-form-dialog-content")
|
|
40
|
+
export class DocksFormDialogContent extends DocksDialogContent {
|
|
41
|
+
@property({ attribute: false })
|
|
42
|
+
fields: FormDialogField[] = [];
|
|
43
|
+
|
|
44
|
+
@property({ type: String })
|
|
45
|
+
message = "";
|
|
46
|
+
|
|
47
|
+
@property({ type: Boolean })
|
|
48
|
+
markdown = false;
|
|
49
|
+
|
|
50
|
+
@state()
|
|
51
|
+
private values: Record<string, string> = {};
|
|
52
|
+
|
|
53
|
+
@state()
|
|
54
|
+
private validationError = "";
|
|
55
|
+
|
|
56
|
+
static styles = [
|
|
57
|
+
...DocksDialogContent.styles,
|
|
58
|
+
css`
|
|
59
|
+
.form-fields {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
gap: 0.75rem;
|
|
63
|
+
min-width: 22rem;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.field label {
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
gap: 0.25rem;
|
|
70
|
+
font-size: var(--wa-font-size-s);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.field-label {
|
|
74
|
+
font-weight: 500;
|
|
75
|
+
color: var(--wa-color-text-normal);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
wa-input {
|
|
79
|
+
width: 100%;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.validation-error {
|
|
83
|
+
color: var(--wa-color-danger-600, #dc2626);
|
|
84
|
+
font-size: var(--wa-font-size-s);
|
|
85
|
+
}
|
|
86
|
+
`,
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
async firstUpdated(changedProperties: PropertyValues) {
|
|
90
|
+
super.firstUpdated(changedProperties);
|
|
91
|
+
const initial: Record<string, string> = {};
|
|
92
|
+
for (const field of this.fields) {
|
|
93
|
+
initial[field.name] = field.value ?? "";
|
|
94
|
+
}
|
|
95
|
+
this.values = initial;
|
|
96
|
+
|
|
97
|
+
await this.updateComplete;
|
|
98
|
+
const input = this.shadowRoot?.querySelector("wa-input");
|
|
99
|
+
const inputEl = (input as HTMLElement & { shadowRoot?: ShadowRoot })?.shadowRoot?.querySelector(
|
|
100
|
+
"input",
|
|
101
|
+
);
|
|
102
|
+
inputEl?.focus();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
getResult(): Record<string, string> {
|
|
106
|
+
return { ...this.values };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
setValidationError(message: string): void {
|
|
110
|
+
this.validationError = message;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private updateField(name: string, value: string): void {
|
|
114
|
+
this.values = { ...this.values, [name]: value };
|
|
115
|
+
if (this.validationError) this.validationError = "";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private handleKeyDown(event: KeyboardEvent): void {
|
|
119
|
+
if (event.key === "Enter") {
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
this.dispatchEvent(new CustomEvent("dialog-ok", { bubbles: true, composed: true }));
|
|
122
|
+
} else if (event.key === "Escape") {
|
|
123
|
+
event.preventDefault();
|
|
124
|
+
this.dispatchEvent(new CustomEvent("dialog-cancel", { bubbles: true, composed: true }));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
render() {
|
|
129
|
+
return html`
|
|
130
|
+
${this.message ? this.renderMessage(this.message, this.markdown) : null}
|
|
131
|
+
<div class="form-fields" @keydown=${this.handleKeyDown}>
|
|
132
|
+
${this.fields.map(
|
|
133
|
+
(field) => html`
|
|
134
|
+
<div class="field">
|
|
135
|
+
<label>
|
|
136
|
+
<span class="field-label">${field.label}</span>
|
|
137
|
+
<wa-input
|
|
138
|
+
type=${field.type ?? "text"}
|
|
139
|
+
.value=${this.values[field.name] ?? ""}
|
|
140
|
+
placeholder=${field.placeholder ?? ""}
|
|
141
|
+
@input=${(event: Event) =>
|
|
142
|
+
this.updateField(
|
|
143
|
+
field.name,
|
|
144
|
+
(event.target as HTMLInputElement).value,
|
|
145
|
+
)}
|
|
146
|
+
></wa-input>
|
|
147
|
+
</label>
|
|
148
|
+
</div>
|
|
149
|
+
`,
|
|
150
|
+
)}
|
|
151
|
+
${this.validationError
|
|
152
|
+
? html`<div class="validation-error">${this.validationError}</div>`
|
|
153
|
+
: null}
|
|
154
|
+
</div>
|
|
155
|
+
`;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
declare global {
|
|
160
|
+
interface HTMLElementTagNameMap {
|
|
161
|
+
"docks-form-dialog-content": DocksFormDialogContent;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function trimValues(values: Record<string, string>): Record<string, string> {
|
|
166
|
+
return Object.fromEntries(
|
|
167
|
+
Object.entries(values).map(([key, value]) => [key, value.trim()]),
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function findFormDialogContent(): DocksFormDialogContent | null {
|
|
172
|
+
return document.querySelector("docks-form-dialog-content");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Validates required fields; returns an error message or null if valid. */
|
|
176
|
+
export function validateFormDialogFields(
|
|
177
|
+
fields: FormDialogField[],
|
|
178
|
+
values: Record<string, string>,
|
|
179
|
+
): string | null {
|
|
180
|
+
for (const field of fields) {
|
|
181
|
+
if (field.required === false) continue;
|
|
182
|
+
if (!values[field.name]?.trim()) {
|
|
183
|
+
return `${field.label} is required.`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
contributionRegistry.registerContribution(DIALOG_CONTRIBUTION_TARGET, {
|
|
190
|
+
id: "form",
|
|
191
|
+
label: "Form",
|
|
192
|
+
buttons: [OK_BUTTON, CANCEL_BUTTON],
|
|
193
|
+
component: (state?: FormDialogState) => {
|
|
194
|
+
if (!state) {
|
|
195
|
+
return html`<div>Error: No form dialog state</div>`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return html`
|
|
199
|
+
<docks-form-dialog-content
|
|
200
|
+
.message=${state.message ?? ""}
|
|
201
|
+
.markdown=${state.markdown ?? false}
|
|
202
|
+
.fields=${state.fields}
|
|
203
|
+
></docks-form-dialog-content>
|
|
204
|
+
`;
|
|
205
|
+
},
|
|
206
|
+
onButton: async (id: string, result: unknown, state?: FormDialogState) => {
|
|
207
|
+
if (!state) {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (id === "ok") {
|
|
212
|
+
const values = trimValues((result ?? {}) as Record<string, string>);
|
|
213
|
+
const error = validateFormDialogFields(state.fields, values);
|
|
214
|
+
if (error) {
|
|
215
|
+
findFormDialogContent()?.setValidationError(error);
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
state.resolve(values);
|
|
219
|
+
} else {
|
|
220
|
+
state.resolve(null);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return true;
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
export function formDialog(options: FormDialogOptions): Promise<Record<string, string> | null> {
|
|
228
|
+
return new Promise((resolve) => {
|
|
229
|
+
dialogService.open("form", { ...options, resolve });
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export async function formDialogRequired(
|
|
234
|
+
options: FormDialogOptions,
|
|
235
|
+
): Promise<Record<string, string>> {
|
|
236
|
+
const values = await formDialog(options);
|
|
237
|
+
if (!values) throw new Error("Cancelled");
|
|
238
|
+
return values;
|
|
239
|
+
}
|
package/src/dialogs/index.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
export { promptDialog } from './prompt-dialog';
|
|
2
|
+
export {
|
|
3
|
+
formDialog,
|
|
4
|
+
formDialogRequired,
|
|
5
|
+
validateFormDialogFields,
|
|
6
|
+
type FormDialogField,
|
|
7
|
+
type FormDialogFieldType,
|
|
8
|
+
type FormDialogOptions,
|
|
9
|
+
} from './form-dialog';
|
|
2
10
|
export { infoDialog } from './info-dialog';
|
|
3
11
|
export { confirmDialog } from './confirm-dialog';
|
|
4
12
|
export { navigableInfoDialog, type NavigableDialogAction } from './navigable-info-dialog';
|