terrier-engine 4.32.4 → 4.34.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.
|
@@ -131,10 +131,9 @@ export default class DivePlotEditor extends ModalPart<DivePlotEditorState> {
|
|
|
131
131
|
parent.div(".tt-flex.column.padded.large-gap", mainColumn => {
|
|
132
132
|
|
|
133
133
|
mainColumn.div(".dd-plot-axes-and-preview.tt-flex.column.gap", axesAndPreview => {
|
|
134
|
-
this.fields.compoundField(axesAndPreview, field
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}).class('plot-title-field')
|
|
134
|
+
this.fields.compoundField(axesAndPreview, 'title', 'plot-title-field')
|
|
135
|
+
.label("Title", '.required')
|
|
136
|
+
.textInput({class: 'shrink plot-title'})
|
|
138
137
|
axesAndPreview.div('.tt-flex.gap', row => {
|
|
139
138
|
this.leftAxisFields.render(row)
|
|
140
139
|
row.part(this.renderPart)
|
|
@@ -55,18 +55,17 @@ class TraceStyleFields extends TerrierFormFields<TraceStyle> {
|
|
|
55
55
|
|
|
56
56
|
render(parent: PartTag) {
|
|
57
57
|
parent.div('.tt-form.tt-flex.gap.wrap', container => {
|
|
58
|
-
this.compoundField(container,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
this.compoundField(container,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
this.compoundField(container,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
})
|
|
58
|
+
this.compoundField(container, 'stroke')
|
|
59
|
+
.label("Color")
|
|
60
|
+
.textInput({placeholder: 'Color'})
|
|
61
|
+
|
|
62
|
+
this.compoundField(container, 'strokeWidth')
|
|
63
|
+
.label("Width")
|
|
64
|
+
.numberInput({placeholder: 'Width'})
|
|
65
|
+
|
|
66
|
+
this.compoundField(container, 'strokeDasharray')
|
|
67
|
+
.label("Dashes")
|
|
68
|
+
.textInput({placeholder: 'Dashes'})
|
|
70
69
|
})
|
|
71
70
|
}
|
|
72
71
|
|
|
@@ -165,10 +164,10 @@ export class DivePlotTraceEditor extends ModalPart<DivePlotTraceState> {
|
|
|
165
164
|
renderContent(parent: PartTag): void {
|
|
166
165
|
parent.div(".tt-form.tt-flex.large-gap.column.padded", mainColumn => {
|
|
167
166
|
// query
|
|
168
|
-
this.fields
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
167
|
+
this.fields
|
|
168
|
+
.compoundField(mainColumn, 'query_id')
|
|
169
|
+
.label("Query")
|
|
170
|
+
.select(this.queryOptions)
|
|
172
171
|
|
|
173
172
|
// axes
|
|
174
173
|
mainColumn.div('.tt-flex.gap', row => {
|
package/package.json
CHANGED
package/terrier/forms.ts
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {FieldConstructor, FormFields, FormPartData, InputType, KeyOfType, SelectOptions} from "tuff-core/forms"
|
|
2
2
|
import {DbErrors} from "./db-client"
|
|
3
3
|
import {PartTag} from "tuff-core/parts"
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
DefaultTagAttrs,
|
|
6
|
+
DivTag,
|
|
7
|
+
InputTag,
|
|
8
|
+
InputTagAttrs,
|
|
9
|
+
SelectTag,
|
|
10
|
+
SelectTagAttrs,
|
|
11
|
+
TextAreaTag,
|
|
12
|
+
TextAreaTagAttrs
|
|
13
|
+
} from "tuff-core/html"
|
|
5
14
|
import TerrierPart from "./parts/terrier-part"
|
|
6
15
|
import GlypPicker from "./parts/glyp-picker"
|
|
7
16
|
import Glyps from "./glyps"
|
|
8
17
|
import Messages from "tuff-core/messages"
|
|
9
18
|
import {Logger} from "tuff-core/logging"
|
|
10
19
|
import Strings from "tuff-core/strings"
|
|
20
|
+
import Theme, {Action, ColorName, IconName} from "./theme"
|
|
21
|
+
import Objects from "tuff-core/objects"
|
|
22
|
+
import {InlineStyle} from "tuff-core/tags"
|
|
23
|
+
import Hints, {Hint, HintRenderOptions} from "./hints"
|
|
11
24
|
|
|
12
25
|
const log = new Logger("TerrierForms")
|
|
13
26
|
|
|
@@ -86,33 +99,27 @@ export class TerrierFormFields<T extends FormPartData> extends FormFields<T> {
|
|
|
86
99
|
})
|
|
87
100
|
}
|
|
88
101
|
|
|
89
|
-
protected
|
|
90
|
-
new(name: string): Field<any, Element>
|
|
91
|
-
}, attrs?: InputTagAttrs): InputTag {
|
|
102
|
+
protected addErrorClass(name: string, attrs: DefaultTagAttrs) {
|
|
92
103
|
if (this.errors && this.errors[name]) {
|
|
93
104
|
attrs ||= {}
|
|
94
105
|
attrs.classes ||= []
|
|
95
106
|
attrs.classes.push('error')
|
|
96
107
|
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
input<Key extends KeyOfType<T,any> & string>(parent: PartTag, type: InputType, name: Key, serializerType: FieldConstructor<any, Element>, attrs: InputTagAttrs={}): InputTag {
|
|
111
|
+
this.addErrorClass(name, attrs)
|
|
97
112
|
return super.input(parent, type, name, serializerType, attrs);
|
|
98
113
|
}
|
|
99
114
|
|
|
100
|
-
select<Key extends
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
attrs.classes ||= []
|
|
104
|
-
attrs.classes.push('error')
|
|
105
|
-
}
|
|
106
|
-
return super.select(parent, name, options, attrs);
|
|
115
|
+
select<Key extends keyof T & string, TSerializer extends FieldConstructor<T[Key], HTMLSelectElement>>(parent: PartTag, name: Key, options?: SelectOptions, attrs: SelectTagAttrs = {}, serializerType?: TSerializer): SelectTag {
|
|
116
|
+
this.addErrorClass(name, attrs)
|
|
117
|
+
return super.select(parent, name, options, attrs, serializerType);
|
|
107
118
|
}
|
|
108
119
|
|
|
109
|
-
textArea<Key extends
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
attrs.classes ||= []
|
|
113
|
-
attrs.classes.push('error')
|
|
114
|
-
}
|
|
115
|
-
return super.textArea(parent, name, attrs);
|
|
120
|
+
textArea<Key extends keyof T & string, TSerializer extends FieldConstructor<T[Key], HTMLTextAreaElement>>(parent: PartTag, name: Key, attrs: SelectTagAttrs = {}, serializerType?: TSerializer): TextAreaTag {
|
|
121
|
+
this.addErrorClass(name, attrs)
|
|
122
|
+
return super.textArea(parent, name, attrs, serializerType);
|
|
116
123
|
}
|
|
117
124
|
|
|
118
125
|
/**
|
|
@@ -142,17 +149,12 @@ export class TerrierFormFields<T extends FormPartData> extends FormFields<T> {
|
|
|
142
149
|
}).emitClick(this.pickGlypKey, {key})
|
|
143
150
|
}
|
|
144
151
|
|
|
152
|
+
compoundField<Key extends keyof T & string>(parent: PartTag, key: Key, ...classes: string[]): CompoundFieldBuilder<T, Key> {
|
|
153
|
+
return new CompoundFieldBuilder(this, parent, key, (this.part as TerrierPart<any>).theme, ...classes)
|
|
154
|
+
}
|
|
145
155
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
* I suppose it's just a slight convenience over having `parent.div(".tt-compound-field"...` everywhere.
|
|
149
|
-
* @param parent
|
|
150
|
-
* @param fun a function that accepts the field as an argument, to actually populate the field
|
|
151
|
-
*/
|
|
152
|
-
compoundField(parent: PartTag, fun: (field: DivTag) => any) {
|
|
153
|
-
return parent.div(".tt-compound-field", field => {
|
|
154
|
-
fun(field)
|
|
155
|
-
})
|
|
156
|
+
fileCompoundField<Key extends KeyOfType<T, File> & string>(parent: PartTag, key: Key, ...classes: string[]): FileCompoundFieldBuilder<T, Key> {
|
|
157
|
+
return new FileCompoundFieldBuilder(this, parent, key, (this.part as TerrierPart<any>).theme, ...classes)
|
|
156
158
|
}
|
|
157
159
|
|
|
158
160
|
/**
|
|
@@ -163,7 +165,7 @@ export class TerrierFormFields<T extends FormPartData> extends FormFields<T> {
|
|
|
163
165
|
* @param title the title to put in the label span
|
|
164
166
|
* @param attrs attributes for the radio input
|
|
165
167
|
*/
|
|
166
|
-
radioLabel<Key extends KeyOfType<T, string> & string>(parent: PartTag, name: Key, value:
|
|
168
|
+
radioLabel<Key extends KeyOfType<T, string> & string>(parent: PartTag, name: Key, value: T[Key], title?: string, attrs: InputTagAttrs = {}) {
|
|
167
169
|
return parent.label('.body-size', label => {
|
|
168
170
|
this.radio(label, name, value, attrs)
|
|
169
171
|
label.span().text(title || value)
|
|
@@ -172,6 +174,147 @@ export class TerrierFormFields<T extends FormPartData> extends FormFields<T> {
|
|
|
172
174
|
|
|
173
175
|
}
|
|
174
176
|
|
|
177
|
+
class CompoundFieldBuilder<T extends Record<string, unknown>, K extends keyof T & string> {
|
|
178
|
+
|
|
179
|
+
field!: DivTag
|
|
180
|
+
|
|
181
|
+
constructor(readonly formFields: TerrierFormFields<T>, readonly parent: PartTag, readonly key: K, readonly theme: Theme, ...classes: string[]) {
|
|
182
|
+
this.field = parent.div('.tt-compound-field')
|
|
183
|
+
// add the error class if this key (or key_id) is in the errors object
|
|
184
|
+
if (formFields.errors && (formFields.errors[key] || formFields.errors[`${key}_id`] || formFields.errors[key.replace(/_id$/,'')])) {
|
|
185
|
+
this.field.class('error')
|
|
186
|
+
}
|
|
187
|
+
if (classes.length) {
|
|
188
|
+
this.field.class(...classes)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
hiddenInput(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
193
|
+
this.formFields.hiddenInput(this.field, this.key, attrs ?? {}, serializerType)
|
|
194
|
+
return this
|
|
195
|
+
}
|
|
196
|
+
textInput(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
197
|
+
this.formFields.textInput(this.field, this.key, attrs ?? {}, serializerType)
|
|
198
|
+
return this
|
|
199
|
+
}
|
|
200
|
+
numberInput(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
201
|
+
this.formFields.numberInput(this.field, this.key, attrs ?? {}, serializerType)
|
|
202
|
+
return this
|
|
203
|
+
}
|
|
204
|
+
emailInput(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
205
|
+
this.formFields.emailInput(this.field, this.key, attrs ?? {}, serializerType)
|
|
206
|
+
return this
|
|
207
|
+
}
|
|
208
|
+
phoneInput(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
209
|
+
this.formFields.phoneInput(this.field, this.key, attrs ?? {}, serializerType)
|
|
210
|
+
return this
|
|
211
|
+
}
|
|
212
|
+
passwordInput(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
213
|
+
this.formFields.passwordInput(this.field, this.key, attrs ?? {}, serializerType)
|
|
214
|
+
return this
|
|
215
|
+
}
|
|
216
|
+
searchInput(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
217
|
+
this.formFields.searchInput(this.field, this.key, attrs ?? {}, serializerType)
|
|
218
|
+
return this
|
|
219
|
+
}
|
|
220
|
+
urlInput(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
221
|
+
this.formFields.urlInput(this.field, this.key, attrs ?? {}, serializerType)
|
|
222
|
+
return this
|
|
223
|
+
}
|
|
224
|
+
textArea(attrs?: TextAreaTagAttrs, serializerType?: FieldConstructor<T[K], HTMLTextAreaElement>): this {
|
|
225
|
+
this.formFields.textArea(this.field, this.key, attrs ?? {}, serializerType)
|
|
226
|
+
return this
|
|
227
|
+
}
|
|
228
|
+
dateInput(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
229
|
+
this.formFields.dateInput(this.field, this.key, attrs ?? {}, serializerType)
|
|
230
|
+
return this
|
|
231
|
+
}
|
|
232
|
+
timeInput(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
233
|
+
this.formFields.timeInput(this.field, this.key, attrs ?? {}, serializerType)
|
|
234
|
+
return this
|
|
235
|
+
}
|
|
236
|
+
dateTimeInput(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
237
|
+
this.formFields.dateTimeInput(this.field, this.key, attrs ?? {}, serializerType)
|
|
238
|
+
return this
|
|
239
|
+
}
|
|
240
|
+
monthInput(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
241
|
+
this.formFields.monthInput(this.field, this.key, attrs ?? {}, serializerType)
|
|
242
|
+
return this
|
|
243
|
+
}
|
|
244
|
+
weekInput(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
245
|
+
this.formFields.weekInput(this.field, this.key, attrs ?? {}, serializerType)
|
|
246
|
+
return this
|
|
247
|
+
}
|
|
248
|
+
radio(value: T[K], attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
249
|
+
this.formFields.radio(this.field, this.key, value, attrs ?? {}, serializerType)
|
|
250
|
+
return this
|
|
251
|
+
}
|
|
252
|
+
checkbox(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
253
|
+
this.formFields.checkbox(this.field, this.key, attrs ?? {}, serializerType)
|
|
254
|
+
return this
|
|
255
|
+
}
|
|
256
|
+
select(selectOptions?: SelectOptions, attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLSelectElement>): this {
|
|
257
|
+
this.formFields.select(this.field, this.key, selectOptions, attrs ?? {}, serializerType)
|
|
258
|
+
return this
|
|
259
|
+
}
|
|
260
|
+
colorInput(attrs?: InputTagAttrs, serializerType?: FieldConstructor<T[K], HTMLInputElement>): this {
|
|
261
|
+
this.formFields.colorInput(this.field, this.key, attrs ?? {}, serializerType)
|
|
262
|
+
return this
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
readonly(text?: string): this {
|
|
266
|
+
this.field.div('.readonly-field', {text: text ?? Objects.safeToString(this.formFields.data[this.key]) })
|
|
267
|
+
return this
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
label(text: string, ...classes: string[]): this {
|
|
271
|
+
this.field.label({ text, htmlFor: `${this.formFields.part.id}-${this.key}`, classes })
|
|
272
|
+
return this
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
tooltip(text: string): this {
|
|
276
|
+
this.field.dataAttr('tooltip', text)
|
|
277
|
+
return this
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
hint(hint: Hint, options?: HintRenderOptions): this {
|
|
281
|
+
Hints.renderHint(this.theme, this.field, hint, options)
|
|
282
|
+
return this
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
icon(icon: IconName, color: ColorName | null = 'secondary'): this {
|
|
286
|
+
this.field.label('.icon-only', label => {
|
|
287
|
+
this.theme.renderIcon(label, icon, color)
|
|
288
|
+
})
|
|
289
|
+
return this
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
css(styles: InlineStyle): this {
|
|
293
|
+
this.field.css(styles)
|
|
294
|
+
return this
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
action(action: Action, color: ColorName = 'link'): this {
|
|
298
|
+
action.classes ||= []
|
|
299
|
+
action.classes.push(color)
|
|
300
|
+
this.theme.renderActions(this.field, action, {iconColor: color})
|
|
301
|
+
return this
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
class(...s: string[]): this {
|
|
305
|
+
this.field.class(...s)
|
|
306
|
+
return this
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
class FileCompoundFieldBuilder<T extends Record<string, unknown>, K extends KeyOfType<T, File> & string> extends CompoundFieldBuilder<T, K> {
|
|
311
|
+
fileInput(attrs?: InputTagAttrs): this {
|
|
312
|
+
this.formFields.fileInput(this.field, this.key, attrs ?? {})
|
|
313
|
+
return this
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
|
|
175
318
|
|
|
176
319
|
|
|
177
320
|
////////////////////////////////////////////////////////////////////////////////
|