terrier-engine 4.4.21 → 4.4.26
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/data-dive/dd-session.ts +52 -0
- package/data-dive/dives/dive-editor.ts +52 -5
- package/data-dive/queries/columns.ts +2 -0
- package/data-dive/queries/filters.ts +46 -14
- package/data-dive/queries/query-editor.ts +4 -1
- package/data-dive/queries/query-form.ts +1 -1
- package/data-dive/queries/tables.ts +49 -8
- package/package.json +1 -1
- package/terrier/dropdowns.ts +9 -3
- package/terrier/glyps.ts +2 -2
- package/terrier/overlays.ts +42 -6
- package/terrier/parts/content-part.ts +4 -5
- package/terrier/parts/page-part.ts +35 -19
- package/terrier/schema.ts +4 -3
- package/terrier/tabs.ts +10 -10
- package/terrier/theme.ts +4 -0
package/data-dive/dd-session.ts
CHANGED
|
@@ -3,19 +3,68 @@ import Api from "../terrier/api"
|
|
|
3
3
|
import {DdDiveGroup} from "./gen/models"
|
|
4
4
|
import {arrays} from "tuff-core";
|
|
5
5
|
import {SelectOptions} from "tuff-core/forms"
|
|
6
|
+
import {Logger} from "tuff-core/logging"
|
|
6
7
|
|
|
8
|
+
const log = new Logger("DD Session")
|
|
7
9
|
|
|
8
10
|
type DdSessionData = {
|
|
9
11
|
user: DdUser
|
|
10
12
|
groupMap: Record<string, DdDiveGroup>
|
|
11
13
|
}
|
|
12
14
|
|
|
15
|
+
const showHintsKey = 'dd-show-hints'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Class that gets added to the body when hints shouldn't be shown.
|
|
19
|
+
* Needs to be negative like this so we can just apply an !important and override any accidental display values.
|
|
20
|
+
*/
|
|
21
|
+
const hideHintsClass = 'dd-hide-hints'
|
|
22
|
+
|
|
13
23
|
/**
|
|
14
24
|
* Stores authentication information as well as various options from the server.
|
|
15
25
|
*/
|
|
16
26
|
export default class DdSession {
|
|
17
27
|
|
|
28
|
+
_showHints = false
|
|
29
|
+
|
|
18
30
|
constructor(readonly data: DdSessionData) {
|
|
31
|
+
// default to showing hints
|
|
32
|
+
const rawShowHints = localStorage.getItem(showHintsKey)
|
|
33
|
+
this._showHints = !rawShowHints || rawShowHints == 'true'
|
|
34
|
+
this.updateShowHints()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Whether to show the helpful hint bubbles all over the place.
|
|
39
|
+
*/
|
|
40
|
+
get showHints() {
|
|
41
|
+
return this._showHints
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Stores the showHints preference in localStorage.
|
|
46
|
+
* @param val whether to show hints
|
|
47
|
+
*/
|
|
48
|
+
set showHints(val: boolean) {
|
|
49
|
+
this._showHints = val
|
|
50
|
+
log.info(`Persisting ${showHintsKey} to ${val}`)
|
|
51
|
+
localStorage.setItem(showHintsKey, val.toString())
|
|
52
|
+
this.updateShowHints()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Updates the DOM with the dd-show-hints class based on the value of this._showHints
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
private updateShowHints() {
|
|
60
|
+
const body = document.querySelector('body')
|
|
61
|
+
if (body) {
|
|
62
|
+
if (this._showHints) {
|
|
63
|
+
body.classList.remove(hideHintsClass)
|
|
64
|
+
} else {
|
|
65
|
+
body.classList.add(hideHintsClass)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
19
68
|
}
|
|
20
69
|
|
|
21
70
|
get user(): DdUser {
|
|
@@ -36,6 +85,9 @@ export default class DdSession {
|
|
|
36
85
|
})
|
|
37
86
|
}
|
|
38
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Gets user session information from the server.
|
|
90
|
+
*/
|
|
39
91
|
static async get(): Promise<DdSession> {
|
|
40
92
|
const data = await Api.safeGet<DdSessionData>("/data_dive/user_session.json", {})
|
|
41
93
|
return new DdSession(data)
|
|
@@ -14,7 +14,8 @@ import {DdDive} from "../gen/models"
|
|
|
14
14
|
import Ids from "../../terrier/ids"
|
|
15
15
|
import Db from "../dd-db"
|
|
16
16
|
import DdSession from "../dd-session"
|
|
17
|
-
import {DiveRunModal} from "./dive-runs"
|
|
17
|
+
import {DiveRunModal} from "./dive-runs"
|
|
18
|
+
import Nav from "tuff-core/nav";
|
|
18
19
|
|
|
19
20
|
const log = new Logger("DiveEditor")
|
|
20
21
|
|
|
@@ -26,6 +27,7 @@ const log = new Logger("DiveEditor")
|
|
|
26
27
|
export type DiveEditorState = {
|
|
27
28
|
schema: SchemaDef
|
|
28
29
|
dive: DdDive
|
|
30
|
+
session: DdSession
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
export default class DiveEditor extends ContentPart<DiveEditorState> {
|
|
@@ -34,17 +36,26 @@ export default class DiveEditor extends ContentPart<DiveEditorState> {
|
|
|
34
36
|
|
|
35
37
|
newQueryKey = messages.untypedKey()
|
|
36
38
|
|
|
39
|
+
static readonly diveChangedKey = messages.untypedKey()
|
|
40
|
+
|
|
37
41
|
queries = new Array<Query>()
|
|
38
42
|
|
|
39
43
|
async init() {
|
|
40
44
|
this.tabs = this.makePart(TabContainerPart, {side: 'top'})
|
|
41
45
|
|
|
42
|
-
this.tabs.
|
|
46
|
+
this.tabs.addBeforeAction({
|
|
43
47
|
title: 'Queries:',
|
|
44
48
|
icon: 'glyp-data_dive_query'
|
|
45
49
|
})
|
|
46
|
-
this.tabs.
|
|
50
|
+
this.tabs.addAfterAction({
|
|
51
|
+
title: "Add Another Query",
|
|
52
|
+
classes: ['dd-hint', 'arrow-right', 'glyp-hint'],
|
|
53
|
+
tooltip: "Each query represents a separate tab in the resulting spreadsheet",
|
|
54
|
+
click: {key: this.newQueryKey}
|
|
55
|
+
})
|
|
56
|
+
this.tabs.addAfterAction({
|
|
47
57
|
icon: 'glyp-plus_outline',
|
|
58
|
+
classes: ['new-query'],
|
|
48
59
|
click: {key: this.newQueryKey}
|
|
49
60
|
})
|
|
50
61
|
|
|
@@ -106,7 +117,9 @@ export class DiveEditorPage extends PagePart<{id: string}> {
|
|
|
106
117
|
editor!: DiveEditor
|
|
107
118
|
session!: DdSession
|
|
108
119
|
|
|
120
|
+
showHintsKey = messages.untypedKey()
|
|
109
121
|
saveKey = messages.untypedKey()
|
|
122
|
+
discardKey = messages.untypedKey()
|
|
110
123
|
runKey = messages.untypedKey()
|
|
111
124
|
|
|
112
125
|
async init() {
|
|
@@ -115,7 +128,7 @@ export class DiveEditorPage extends PagePart<{id: string}> {
|
|
|
115
128
|
const schema = await Schema.get()
|
|
116
129
|
this.session = await DdSession.get()
|
|
117
130
|
const dive = await Dives.get(this.state.id)
|
|
118
|
-
this.editor = this.makePart(DiveEditor, {schema, dive})
|
|
131
|
+
this.editor = this.makePart(DiveEditor, {schema, dive, session: this.session})
|
|
119
132
|
|
|
120
133
|
this.mainContentWidth = 'wide'
|
|
121
134
|
|
|
@@ -129,9 +142,25 @@ export class DiveEditorPage extends PagePart<{id: string}> {
|
|
|
129
142
|
icon: 'glyp-data_dive'
|
|
130
143
|
})
|
|
131
144
|
|
|
145
|
+
this.addToolbarInput('show-hints', 'checkbox', {
|
|
146
|
+
title: "Hints",
|
|
147
|
+
icon: 'glyp-hint',
|
|
148
|
+
defaultValue: this.session.showHints.toString(),
|
|
149
|
+
onChangeKey: this.showHintsKey,
|
|
150
|
+
onInputKey: this.showHintsKey
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
this.addAction({
|
|
154
|
+
title: 'Discard',
|
|
155
|
+
icon: 'glyp-cancelled',
|
|
156
|
+
classes: ['discard-dive-action'],
|
|
157
|
+
click: {key: this.discardKey}
|
|
158
|
+
}, 'tertiary')
|
|
159
|
+
|
|
132
160
|
this.addAction({
|
|
133
161
|
title: 'Save',
|
|
134
|
-
icon: 'glyp-
|
|
162
|
+
icon: 'glyp-complete',
|
|
163
|
+
classes: ['save-dive-action'],
|
|
135
164
|
click: {key: this.saveKey}
|
|
136
165
|
}, 'tertiary')
|
|
137
166
|
|
|
@@ -141,10 +170,27 @@ export class DiveEditorPage extends PagePart<{id: string}> {
|
|
|
141
170
|
click: {key: this.runKey}
|
|
142
171
|
}, 'tertiary')
|
|
143
172
|
|
|
173
|
+
this.onClick(this.discardKey, _ => {
|
|
174
|
+
log.info("Discarding dive changes")
|
|
175
|
+
Nav.visit(location.href)
|
|
176
|
+
})
|
|
177
|
+
|
|
144
178
|
this.onClick(this.saveKey, _ => this.save())
|
|
145
179
|
|
|
146
180
|
this.onClick(this.runKey, _ => this.run())
|
|
147
181
|
|
|
182
|
+
this.onInput(this.showHintsKey, m => {
|
|
183
|
+
// TODO: fix emitInput and emitChange to return the correct value in tuff
|
|
184
|
+
const showHints = (m.event.target as HTMLInputElement).checked
|
|
185
|
+
log.info(`Show hints input to ${showHints}`, m)
|
|
186
|
+
this.session.showHints = showHints
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
this.listenMessage(DiveEditor.diveChangedKey, _ => {
|
|
190
|
+
log.info("Dive changed")
|
|
191
|
+
this.element?.classList.add('changed')
|
|
192
|
+
}, {attach: 'passive'})
|
|
193
|
+
|
|
148
194
|
this.dirty()
|
|
149
195
|
}
|
|
150
196
|
|
|
@@ -162,6 +208,7 @@ export class DiveEditorPage extends PagePart<{id: string}> {
|
|
|
162
208
|
const res = await Db().upsert('dd_dive', dive)
|
|
163
209
|
if (res.status == 'success') {
|
|
164
210
|
this.successToast(`Saved Dive!`)
|
|
211
|
+
this.element?.classList.remove('changed')
|
|
165
212
|
}
|
|
166
213
|
else {
|
|
167
214
|
this.alertToast(res.message)
|
|
@@ -9,6 +9,7 @@ import Objects from "tuff-core/objects"
|
|
|
9
9
|
import {ModalPart} from "../../terrier/modals";
|
|
10
10
|
import TerrierFormPart from "../../terrier/parts/terrier-form-part"
|
|
11
11
|
import {Dropdown} from "../../terrier/dropdowns"
|
|
12
|
+
import DiveEditor from "../dives/dive-editor"
|
|
12
13
|
|
|
13
14
|
const log = new Logger("Columns")
|
|
14
15
|
|
|
@@ -247,6 +248,7 @@ export class ColumnsEditorModal extends ModalPart<ColumnsEditorState> {
|
|
|
247
248
|
})
|
|
248
249
|
const tableData = await this.tableFields.serialize()
|
|
249
250
|
this.state.tableView.updateColumns(columns, tableData.prefix)
|
|
251
|
+
this.emitMessage(DiveEditor.diveChangedKey, {})
|
|
250
252
|
this.pop()
|
|
251
253
|
}
|
|
252
254
|
|
|
@@ -9,8 +9,9 @@ import inflection from "inflection"
|
|
|
9
9
|
import {ModalPart} from "../../terrier/modals";
|
|
10
10
|
import TerrierFormPart from "../../terrier/parts/terrier-form-part"
|
|
11
11
|
import {Dropdown} from "../../terrier/dropdowns"
|
|
12
|
-
import dayjs from "dayjs"
|
|
13
|
-
import Format from "../../terrier/format"
|
|
12
|
+
import dayjs from "dayjs"
|
|
13
|
+
import Format from "../../terrier/format"
|
|
14
|
+
import DiveEditor from "../dives/dive-editor"
|
|
14
15
|
|
|
15
16
|
const log = new Logger("Filters")
|
|
16
17
|
|
|
@@ -170,7 +171,8 @@ export class FiltersEditorModal extends ModalPart<FiltersEditorState> {
|
|
|
170
171
|
|
|
171
172
|
this.addAction({
|
|
172
173
|
title: 'Add Filter',
|
|
173
|
-
icon: 'glyp-
|
|
174
|
+
icon: 'glyp-plus_outline',
|
|
175
|
+
classes: ['add-filter', 'secondary'],
|
|
174
176
|
click: {key: addKey}
|
|
175
177
|
}, 'secondary')
|
|
176
178
|
|
|
@@ -183,12 +185,7 @@ export class FiltersEditorModal extends ModalPart<FiltersEditorState> {
|
|
|
183
185
|
})
|
|
184
186
|
|
|
185
187
|
this.onClick(addKey, m => {
|
|
186
|
-
|
|
187
|
-
log.info(`Adding ${filter.filter_type} filter`, filter)
|
|
188
|
-
this.addState(filter)
|
|
189
|
-
this.updateFilterEditors()
|
|
190
|
-
}
|
|
191
|
-
this.toggleDropdown(AddFilterDropdown, {modelDef: this.modelDef, callback: onSelected}, m.event.target)
|
|
188
|
+
this.showAddFilterDropdown(m.event.target! as HTMLElement)
|
|
192
189
|
})
|
|
193
190
|
}
|
|
194
191
|
|
|
@@ -213,12 +210,30 @@ export class FiltersEditorModal extends ModalPart<FiltersEditorState> {
|
|
|
213
210
|
}
|
|
214
211
|
}
|
|
215
212
|
|
|
213
|
+
showAddFilterDropdown(target: HTMLElement | null) {
|
|
214
|
+
const onSelected = (filter: Filter) => {
|
|
215
|
+
log.info(`Adding ${filter.filter_type} filter`, filter)
|
|
216
|
+
this.addState(filter)
|
|
217
|
+
this.updateFilterEditors()
|
|
218
|
+
}
|
|
219
|
+
this.toggleDropdown(AddFilterDropdown, {modelDef: this.modelDef, callback: onSelected}, target)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
update(elem: HTMLElement) {
|
|
223
|
+
super.update(elem)
|
|
224
|
+
|
|
225
|
+
// if there are no filters, show the dropdown right away
|
|
226
|
+
if (this.filterStates.length == 0) {
|
|
227
|
+
this.showAddFilterDropdown(null)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
216
230
|
|
|
217
231
|
save() {
|
|
218
232
|
const filters = this.filterStates.map(state => {
|
|
219
233
|
return Objects.omit(state, 'schema', 'filtersEditor', 'id') as Filter
|
|
220
234
|
})
|
|
221
235
|
this.state.tableView.updateFilters(filters)
|
|
236
|
+
this.emitMessage(DiveEditor.diveChangedKey, {})
|
|
222
237
|
this.pop()
|
|
223
238
|
}
|
|
224
239
|
|
|
@@ -538,7 +553,7 @@ type AddFilterCallback = (filter: Filter) => any
|
|
|
538
553
|
const columnSelectedKey = messages.typedKey<{column: string}>()
|
|
539
554
|
|
|
540
555
|
class AddFilterDropdown extends Dropdown<{modelDef: ModelDef, callback: AddFilterCallback}> {
|
|
541
|
-
columns!:
|
|
556
|
+
columns!: ColumnDef[]
|
|
542
557
|
|
|
543
558
|
get autoClose(): boolean {
|
|
544
559
|
return true
|
|
@@ -547,7 +562,11 @@ class AddFilterDropdown extends Dropdown<{modelDef: ModelDef, callback: AddFilte
|
|
|
547
562
|
async init() {
|
|
548
563
|
await super.init()
|
|
549
564
|
|
|
550
|
-
this.columns = Object.
|
|
565
|
+
this.columns = arrays.sortByFunction(Object.values(this.state.modelDef.columns), col => {
|
|
566
|
+
const visibility = col.metadata?.visibility
|
|
567
|
+
const sort = visibility == 'common' ? '0' : '1'
|
|
568
|
+
return `${sort}${col.name}`
|
|
569
|
+
})
|
|
551
570
|
|
|
552
571
|
this.onClick(columnSelectedKey, m => {
|
|
553
572
|
const column = m.data.column
|
|
@@ -574,7 +593,7 @@ class AddFilterDropdown extends Dropdown<{modelDef: ModelDef, callback: AddFilte
|
|
|
574
593
|
}
|
|
575
594
|
|
|
576
595
|
get parentClasses(): Array<string> {
|
|
577
|
-
return super.parentClasses.concat(['dd-select-columns-dropdown']);
|
|
596
|
+
return super.parentClasses.concat(['dd-select-columns-dropdown', 'tt-actions-dropdown']);
|
|
578
597
|
}
|
|
579
598
|
|
|
580
599
|
renderContent(parent: PartTag) {
|
|
@@ -582,9 +601,22 @@ class AddFilterDropdown extends Dropdown<{modelDef: ModelDef, callback: AddFilte
|
|
|
582
601
|
header.i(".glyp-columns")
|
|
583
602
|
header.span().text("Select a Column")
|
|
584
603
|
})
|
|
604
|
+
let showingCommon = true
|
|
585
605
|
for (const column of this.columns) {
|
|
586
|
-
|
|
587
|
-
|
|
606
|
+
const isCommon = column.metadata?.visibility == 'common'
|
|
607
|
+
parent.a(a => {
|
|
608
|
+
a.div('.title').text(column.name)
|
|
609
|
+
const desc = column.metadata?.description
|
|
610
|
+
if (desc?.length) {
|
|
611
|
+
a.div('.subtitle').text(desc)
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// show a border between common and uncommon columns
|
|
615
|
+
if (showingCommon && !isCommon) {
|
|
616
|
+
a.class('border-top')
|
|
617
|
+
}
|
|
618
|
+
}).emitClick(columnSelectedKey, {column: column.name})
|
|
619
|
+
showingCommon = isCommon
|
|
588
620
|
}
|
|
589
621
|
}
|
|
590
622
|
|
|
@@ -39,7 +39,10 @@ class SettingsPart extends ContentPart<SubEditorState> {
|
|
|
39
39
|
|
|
40
40
|
renderContent(parent: PartTag) {
|
|
41
41
|
parent.part(this.form)
|
|
42
|
-
parent.div('.tt-flex.justify-end', row => {
|
|
42
|
+
parent.div('.tt-flex.gap.align-center.justify-end', row => {
|
|
43
|
+
row.div('.dd-hint.glyp-hint', hint => {
|
|
44
|
+
hint.div('.title').text("These settings only apply to this query, not the dive as a whole")
|
|
45
|
+
})
|
|
43
46
|
row.a('.alert.tt-flex', a => {
|
|
44
47
|
a.i('.glyp-delete')
|
|
45
48
|
a.span({text: "Delete"})
|
|
@@ -3,12 +3,13 @@ import Schema, {BelongsToDef, ModelDef, SchemaDef} from "../../terrier/schema"
|
|
|
3
3
|
import inflection from "inflection"
|
|
4
4
|
import Filters, {Filter, FilterInput, FiltersEditorModal} from "./filters"
|
|
5
5
|
import Columns, {ColumnRef, ColumnsEditorModal} from "./columns"
|
|
6
|
-
import {messages} from "tuff-core"
|
|
6
|
+
import {arrays, messages} from "tuff-core"
|
|
7
7
|
import {Logger} from "tuff-core/logging"
|
|
8
8
|
import ContentPart from "../../terrier/parts/content-part"
|
|
9
9
|
import {ActionsDropdown} from "../../terrier/dropdowns"
|
|
10
10
|
import {ModalPart} from "../../terrier/modals"
|
|
11
11
|
import TerrierFormPart from "../../terrier/parts/terrier-form-part"
|
|
12
|
+
import DiveEditor from "../dives/dive-editor";
|
|
12
13
|
|
|
13
14
|
const log = new Logger("Tables")
|
|
14
15
|
|
|
@@ -98,19 +99,37 @@ export class TableView<T extends TableRef> extends ContentPart<{ schema: SchemaD
|
|
|
98
99
|
this.app.showModal(FiltersEditorModal, {schema: this.schema, tableView: this as TableView<TableRef>})
|
|
99
100
|
})
|
|
100
101
|
|
|
102
|
+
// show the new join dropdown
|
|
101
103
|
this.onClick(this.newJoinedKey, m => {
|
|
102
104
|
log.info(`Adding join to ${this.displayName}`)
|
|
103
105
|
|
|
104
106
|
// only show belongs-tos that aren't already joined
|
|
105
107
|
const existingJoins = new Set(Object.keys(this.table.joins || []))
|
|
106
|
-
|
|
108
|
+
|
|
109
|
+
const newJoins = Object.values(this.modelDef.belongs_to)
|
|
107
110
|
.filter(bt => !existingJoins.has(bt.name))
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
|
|
112
|
+
// show the common tables at the top
|
|
113
|
+
let showingCommon = true
|
|
114
|
+
const actions = arrays.sortByFunction(newJoins, bt => {
|
|
115
|
+
const model = this.schema.models[bt.model]
|
|
116
|
+
const common = model.metadata?.visibility == 'common' ? '0' : '1'
|
|
117
|
+
return `${common}${bt.name}`
|
|
118
|
+
})
|
|
119
|
+
.map(bt => {
|
|
120
|
+
const model = this.schema.models[bt.model]
|
|
121
|
+
const isCommon = model.metadata?.visibility == 'common'
|
|
122
|
+
// put a border between the common and uncommon
|
|
123
|
+
const classes = showingCommon && !isCommon ? ['border-top'] : []
|
|
124
|
+
showingCommon = isCommon
|
|
125
|
+
return {
|
|
126
|
+
title: Schema.belongsToDisplay(bt),
|
|
127
|
+
subtitle: model.metadata?.description,
|
|
128
|
+
classes,
|
|
129
|
+
click: {key: this.createJoinedKey, data: {name: bt.name}}
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
|
|
114
133
|
|
|
115
134
|
// don't show the dropdown if there are no more belongs-tos left
|
|
116
135
|
if (actions.length) {
|
|
@@ -121,6 +140,7 @@ export class TableView<T extends TableRef> extends ContentPart<{ schema: SchemaD
|
|
|
121
140
|
}
|
|
122
141
|
})
|
|
123
142
|
|
|
143
|
+
// create a new join
|
|
124
144
|
this.onClick(this.createJoinedKey, m => {
|
|
125
145
|
const belongsTo = this.modelDef.belongs_to[m.data.name]
|
|
126
146
|
if (belongsTo) {
|
|
@@ -195,7 +215,17 @@ export class TableView<T extends TableRef> extends ContentPart<{ schema: SchemaD
|
|
|
195
215
|
a.div().text("Join")
|
|
196
216
|
a.i('.glyp-belongs_to')
|
|
197
217
|
}).emitClick(this.newJoinedKey)
|
|
218
|
+
|
|
219
|
+
// join hint
|
|
220
|
+
if (!Object.keys(this.table.joins || {}).length) {
|
|
221
|
+
panel.a('.dd-hint.joins.arrow-top.glyp-hint', hint => {
|
|
222
|
+
hint.div('.title').text("Join More Tables")
|
|
223
|
+
})
|
|
224
|
+
.emitClick(this.newJoinedKey)
|
|
225
|
+
.data({tooltip: "Include data from other tables that are related to this one"})
|
|
226
|
+
}
|
|
198
227
|
})
|
|
228
|
+
|
|
199
229
|
}
|
|
200
230
|
|
|
201
231
|
renderColumns(parent: PartTag) {
|
|
@@ -216,6 +246,11 @@ export class TableView<T extends TableRef> extends ContentPart<{ schema: SchemaD
|
|
|
216
246
|
}
|
|
217
247
|
else {
|
|
218
248
|
section.div('.line.empty').div().text('None')
|
|
249
|
+
section.div('.dd-hint-container', hintContainer => {
|
|
250
|
+
hintContainer.div('dd-hint.glyp-hint', hint => {
|
|
251
|
+
hint.div('.hint-title').text("Add Columns")
|
|
252
|
+
})
|
|
253
|
+
})
|
|
219
254
|
}
|
|
220
255
|
}).emitClick(this.editColumnsKey, {})
|
|
221
256
|
}
|
|
@@ -234,6 +269,11 @@ export class TableView<T extends TableRef> extends ContentPart<{ schema: SchemaD
|
|
|
234
269
|
}
|
|
235
270
|
} else {
|
|
236
271
|
section.div('.line.empty').text('None')
|
|
272
|
+
section.div('.dd-hint-container', hintContainer => {
|
|
273
|
+
hintContainer.div('dd-hint.glyp-hint', hint => {
|
|
274
|
+
hint.div('.hint-title').text("Add Filters")
|
|
275
|
+
})
|
|
276
|
+
})
|
|
237
277
|
}
|
|
238
278
|
}).emitClick(this.editFiltersKey, {})
|
|
239
279
|
}
|
|
@@ -372,6 +412,7 @@ class JoinedTableEditorModal extends ModalPart<JoinedTableEditorState> {
|
|
|
372
412
|
this.onClick(this.applyKey, async _ => {
|
|
373
413
|
const table = await this.form.serialize()
|
|
374
414
|
this.state.callback(table)
|
|
415
|
+
this.emitMessage(DiveEditor.diveChangedKey, {})
|
|
375
416
|
this.pop()
|
|
376
417
|
})
|
|
377
418
|
}
|
package/package.json
CHANGED
package/terrier/dropdowns.ts
CHANGED
|
@@ -78,9 +78,15 @@ export abstract class Dropdown<TState> extends TerrierPart<TState> {
|
|
|
78
78
|
|
|
79
79
|
update(_elem: HTMLElement) {
|
|
80
80
|
const content = _elem.querySelector('.tt-dropdown-content')
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
if (content) {
|
|
82
|
+
if (this.anchorTarget) {
|
|
83
|
+
log.info(`Anchoring dropdown`, content, this.anchorTarget)
|
|
84
|
+
Overlays.anchorElement(content as HTMLElement, this.anchorTarget)
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
// no anchor, just center it on the screen
|
|
88
|
+
Overlays.centerElement(content as HTMLElement)
|
|
89
|
+
}
|
|
84
90
|
content.classList.add('show')
|
|
85
91
|
}
|
|
86
92
|
}
|
package/terrier/glyps.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// This file was automatically generated by glyps:compile on
|
|
1
|
+
// This file was automatically generated by glyps:compile on 07/21/23 5:50 PM, DO NOT MANUALLY EDIT!
|
|
2
2
|
|
|
3
|
-
const names = ['glyp-abacus', 'glyp-account', 'glyp-accounts_payable', 'glyp-accounts_receivable', 'glyp-action_log', 'glyp-activate', 'glyp-active', 'glyp-activity_high', 'glyp-activity_low', 'glyp-activity_medium', 'glyp-activity_none', 'glyp-address1', 'glyp-adjustment', 'glyp-admin', 'glyp-administration', 'glyp-air_freshener', 'glyp-alert', 'glyp-align_horizontal', 'glyp-align_vertical', 'glyp-anchor', 'glyp-announcement', 'glyp-app_update', 'glyp-append_selection', 'glyp-appointment', 'glyp-appointment_setup', 'glyp-archive', 'glyp-arrow_down', 'glyp-arrow_left', 'glyp-arrow_right', 'glyp-arrow_transfer', 'glyp-arrow_up', 'glyp-ascending', 'glyp-attachment', 'glyp-audit_analyzer', 'glyp-autopay', 'glyp-availability', 'glyp-background_batch', 'glyp-background_job', 'glyp-bait_for_demolition', 'glyp-bar_chart', 'glyp-barcode', 'glyp-bat_exclusion', 'glyp-bed_bug_fumigation', 'glyp-begin_service', 'glyp-belongs_to', 'glyp-billing', 'glyp-billing_office', 'glyp-billing_terms', 'glyp-billto', 'glyp-bioremediation', 'glyp-bird_exclusion', 'glyp-black_light', 'glyp-branch', 'glyp-branch_bats', 'glyp-branch_birds', 'glyp-branch_general', 'glyp-branch_irrigation', 'glyp-branch_landscape', 'glyp-branch_multi_housing', 'glyp-branch_office', 'glyp-branch_orders', 'glyp-branch_residential', 'glyp-branch_sales', 'glyp-branch_termites', 'glyp-branch_weeds', 'glyp-branch_wildlife', 'glyp-build', 'glyp-calculate', 'glyp-calendar', 'glyp-calendar_branch', 'glyp-calendar_map', 'glyp-calendar_rolling', 'glyp-calendar_share', 'glyp-calendar_snap', 'glyp-california', 'glyp-call', 'glyp-callback', 'glyp-camera', 'glyp-cancellation', 'glyp-cancelled', 'glyp-card_american_express', 'glyp-card_discover', 'glyp-card_mastercard', 'glyp-card_visa', 'glyp-catalog', 'glyp-caught', 'glyp-cert', 'glyp-check_all', 'glyp-check_in', 'glyp-check_in_service', 'glyp-checked', 'glyp-checked_empty', 'glyp-checkmark', 'glyp-checkmark_filled', 'glyp-checkout', 'glyp-chemical', 'glyp-chemical_authorization', 'glyp-chemical_backpack_sprayer', 'glyp-chemical_biological_controls', 'glyp-chemical_disinfectant', 'glyp-chemical_fertilizer', 'glyp-chemical_flying_insect_bait', 'glyp-chemical_fumigants', 'glyp-chemical_insect_aerosol', 'glyp-chemical_insect_bait', 'glyp-chemical_insect_dust', 'glyp-chemical_insect_granules', 'glyp-chemical_insect_spray', 'glyp-chemical_label', 'glyp-chemical_mole_bait', 'glyp-chemical_pest_bird_control', 'glyp-chemical_pheromone_products', 'glyp-chemical_power_sprayer', 'glyp-chemical_rodenticide_block', 'glyp-chemical_rodenticide_non_block', 'glyp-chemical_sds', 'glyp-chemical_tree_products', 'glyp-chemical_weed_control', 'glyp-chemical_wildlife_repellents', 'glyp-chemical_wildlife_toxicants', 'glyp-chevron_down', 'glyp-chevron_left', 'glyp-chevron_right', 'glyp-chevron_up', 'glyp-city', 'glyp-click', 'glyp-client_actions', 'glyp-client_pest_sightings', 'glyp-client_required', 'glyp-close', 'glyp-cloud', 'glyp-clypboard', 'glyp-clypedia', 'glyp-clypmart', 'glyp-code', 'glyp-code_details', 'glyp-collections', 'glyp-columns', 'glyp-comment', 'glyp-comment_filled', 'glyp-commission_origin', 'glyp-commissions', 'glyp-company_setup', 'glyp-compass', 'glyp-complete', 'glyp-complete_certificate', 'glyp-complete_enrollment', 'glyp-condition_appliance_machinery', 'glyp-condition_customer', 'glyp-condition_device', 'glyp-condition_door_window', 'glyp-condition_dumpster', 'glyp-condition_entry_point', 'glyp-condition_evidence', 'glyp-condition_exclusion', 'glyp-condition_exterior', 'glyp-condition_interior', 'glyp-condition_plumbing', 'glyp-condition_prep_storage', 'glyp-condition_rodent_evidence', 'glyp-condition_rodent_exclusion', 'glyp-condition_roof_ceiling', 'glyp-condition_structure', 'glyp-condition_supplies', 'glyp-condition_termites', 'glyp-condition_ventilation', 'glyp-condition_wall_floor', 'glyp-condition_wildlife', 'glyp-conditions', 'glyp-consolidate', 'glyp-construction', 'glyp-contract', 'glyp-contract_cancellation', 'glyp-contract_overview', 'glyp-conversation', 'glyp-copesan', 'glyp-copy', 'glyp-credit_memo', 'glyp-credit_non_revenue', 'glyp-credit_production', 'glyp-credit_prospect', 'glyp-credit_sales_bonus', 'glyp-crew', 'glyp-cumulative', 'glyp-cursor', 'glyp-custom_form', 'glyp-customer', 'glyp-customer_service', 'glyp-dashboard', 'glyp-data_dive', 'glyp-data_dive_query', 'glyp-data_dives', 'glyp-database', 'glyp-dead_animal_removal', 'glyp-default', 'glyp-delete', 'glyp-demo_mode', 'glyp-descending', 'glyp-design', 'glyp-desktop', 'glyp-detail_report', 'glyp-developer', 'glyp-device', 'glyp-device_sync', 'glyp-differential', 'glyp-disable', 'glyp-disabled_filled', 'glyp-distribute_horizontal', 'glyp-distribute_vertical', 'glyp-division', 'glyp-document', 'glyp-documentation', 'glyp-documents', 'glyp-download', 'glyp-driving', 'glyp-duplicate', 'glyp-duplicate_location', 'glyp-duration', 'glyp-edit', 'glyp-email', 'glyp-employment', 'glyp-entertainment_public_venues', 'glyp-equipment_bear_trap', 'glyp-equipment_flying_insect_bait_station', 'glyp-equipment_flying_insect_light_trap', 'glyp-equipment_insect_monitor', 'glyp-equipment_lethal_animal_trap', 'glyp-equipment_live_animal_trap', 'glyp-equipment_live_rodent_trap', 'glyp-equipment_maintenance', 'glyp-equipment_map_viewer', 'glyp-equipment_maps', 'glyp-equipment_mosquito_trap', 'glyp-equipment_other', 'glyp-equipment_pheromone_trap', 'glyp-equipment_pick_up', 'glyp-equipment_rodent_bait', 'glyp-equipment_rodent_trap', 'glyp-equipment_termite_bait_station', 'glyp-equipment_termite_monitor', 'glyp-exclusion', 'glyp-expand', 'glyp-expiring', 'glyp-exterior', 'glyp-extras', 'glyp-facebook', 'glyp-farm', 'glyp-farm_grain_seed', 'glyp-fast_and_frequent', 'glyp-favorite', 'glyp-feedback', 'glyp-field_guide', 'glyp-field_training', 'glyp-file_blank', 'glyp-file_html', 'glyp-file_image', 'glyp-file_pdf', 'glyp-file_printable', 'glyp-file_spreadsheet', 'glyp-file_text', 'glyp-filter', 'glyp-finance', 'glyp-finding', 'glyp-folder', 'glyp-followup', 'glyp-food_bev_pharma', 'glyp-formula', 'glyp-fuel', 'glyp-fumigation', 'glyp-function', 'glyp-function_aggregate', 'glyp-function_time', 'glyp-gain_loss', 'glyp-generate_invoices', 'glyp-generate_orders', 'glyp-geo', 'glyp-geo_heat_map', 'glyp-google', 'glyp-google_calendar', 'glyp-government', 'glyp-grain_seed', 'glyp-grid', 'glyp-grouped', 'glyp-grouping_combine', 'glyp-grouping_separate', 'glyp-has_many', 'glyp-health_care', 'glyp-heat_map', 'glyp-heat_treatment', 'glyp-help', 'glyp-hidden', 'glyp-honeybee_removal', 'glyp-housing', 'glyp-in_progress', 'glyp-incomplete_certificate', 'glyp-industrial', 'glyp-info', 'glyp-information_technology', 'glyp-inspect_map', 'glyp-inspections', 'glyp-insulation', 'glyp-interior', 'glyp-invoice', 'glyp-invoice_review', 'glyp-irrigation_install', 'glyp-irrigation_maintenance', 'glyp-items', 'glyp-job', 'glyp-join', 'glyp-join_inner', 'glyp-join_left', 'glyp-justice', 'glyp-k9_inspection', 'glyp-key', 'glyp-label_bottom', 'glyp-label_left', 'glyp-label_right', 'glyp-label_top', 'glyp-labor', 'glyp-landscape', 'glyp-landscape_maintenance', 'glyp-laptop', 'glyp-lead', 'glyp-lead_listing', 'glyp-lead_source', 'glyp-leads_report', 'glyp-learning_hub', 'glyp-ledger', 'glyp-legal', 'glyp-license', 'glyp-lifetime', 'glyp-line_cap_arrow', 'glyp-line_cap_bar', 'glyp-line_cap_circle', 'glyp-line_cap_none', 'glyp-line_caps', 'glyp-line_style', 'glyp-link', 'glyp-location', 'glyp-location_charge', 'glyp-location_credit', 'glyp-location_kind', 'glyp-location_message', 'glyp-location_origin', 'glyp-location_tags', 'glyp-locations', 'glyp-locked', 'glyp-lodging', 'glyp-log_in', 'glyp-log_out', 'glyp-log_report', 'glyp-logbook', 'glyp-logbook_documents', 'glyp-lot_number', 'glyp-manager', 'glyp-map', 'glyp-markdown', 'glyp-market', 'glyp-materials', 'glyp-mattress_encasement', 'glyp-merge', 'glyp-message_billing', 'glyp-message_collections', 'glyp-message_complaint', 'glyp-message_misc', 'glyp-message_technician', 'glyp-messages', 'glyp-misc', 'glyp-miscellaneous', 'glyp-move_order', 'glyp-mowing', 'glyp-multi_housing', 'glyp-mute', 'glyp-navicon', 'glyp-new_location', 'glyp-no_charge', 'glyp-no_service', 'glyp-note', 'glyp-note_access', 'glyp-note_billing', 'glyp-note_collection', 'glyp-note_complaint', 'glyp-note_directions', 'glyp-note_focused', 'glyp-note_office', 'glyp-note_preservice', 'glyp-note_sales', 'glyp-note_service', 'glyp-notification', 'glyp-number', 'glyp-office', 'glyp-office_government', 'glyp-office_training', 'glyp-on_demand', 'glyp-on_demand_order', 'glyp-open', 'glyp-open_invoice', 'glyp-operations', 'glyp-options', 'glyp-order_actions', 'glyp-order_approved', 'glyp-order_batch', 'glyp-order_editor', 'glyp-order_pending', 'glyp-order_series', 'glyp-order_unapproved', 'glyp-org_structure', 'glyp-org_unit', 'glyp-org_unit_settings', 'glyp-origin', 'glyp-origin_client', 'glyp-origin_tech', 'glyp-outlook', 'glyp-overlap', 'glyp-password', 'glyp-past_due', 'glyp-paused', 'glyp-pay_brackets', 'glyp-payment', 'glyp-payment_ach', 'glyp-payment_application_apply', 'glyp-payment_application_correction', 'glyp-payment_application_initial', 'glyp-payment_application_refund', 'glyp-payment_application_transfer', 'glyp-payment_application_unapply', 'glyp-payment_application_unapply_all', 'glyp-payment_applications', 'glyp-payment_batch', 'glyp-payment_cash', 'glyp-payment_check', 'glyp-payment_coupon', 'glyp-payment_credit', 'glyp-payment_discount', 'glyp-payment_eft', 'glyp-payment_finance_charge', 'glyp-payments', 'glyp-payroll', 'glyp-pdf', 'glyp-pending', 'glyp-periodic', 'glyp-periodic_assessment', 'glyp-permission', 'glyp-pest', 'glyp-pest_ants', 'glyp-pest_bats', 'glyp-pest_bed_bugs', 'glyp-pest_birds', 'glyp-pest_crawling_insects', 'glyp-pest_dermestids', 'glyp-pest_fall_invaders', 'glyp-pest_flying_insects', 'glyp-pest_moles', 'glyp-pest_mosquitoes', 'glyp-pest_nuisance_animals', 'glyp-pest_ornamental', 'glyp-pest_ornamental_diseases', 'glyp-pest_roaches', 'glyp-pest_rodents', 'glyp-pest_scorpions', 'glyp-pest_snakes', 'glyp-pest_spiders', 'glyp-pest_stinging_insects', 'glyp-pest_stored_product_pests', 'glyp-pest_ticks_and_fleas', 'glyp-pest_viruses_and_bacteria', 'glyp-pest_weeds', 'glyp-pest_wood_destroyers', 'glyp-phone', 'glyp-pick_date', 'glyp-pick_list', 'glyp-platform_android', 'glyp-platform_ios', 'glyp-platform_macos', 'glyp-platform_windows', 'glyp-play', 'glyp-plus', 'glyp-plus_filled', 'glyp-plus_outline', 'glyp-portal', 'glyp-price_increase', 'glyp-price_increase_alt', 'glyp-pricing_table', 'glyp-print', 'glyp-privacy', 'glyp-product_sale', 'glyp-production', 'glyp-professional_consultation', 'glyp-program', 'glyp-program_elements', 'glyp-program_initiation', 'glyp-program_order', 'glyp-program_review', 'glyp-promo', 'glyp-proposal', 'glyp-protection', 'glyp-purchase_order', 'glyp-quality', 'glyp-query', 'glyp-radio_checked', 'glyp-radio_empty', 'glyp-reassign', 'glyp-receipt', 'glyp-recent', 'glyp-recommendation', 'glyp-record_details', 'glyp-recurring_revenue', 'glyp-redo', 'glyp-refresh', 'glyp-refund', 'glyp-region', 'glyp-released', 'glyp-remove', 'glyp-renewal', 'glyp-report', 'glyp-required_input', 'glyp-reschedule', 'glyp-restaurant_bar', 'glyp-revenue', 'glyp-review', 'glyp-rfid', 'glyp-ride_along', 'glyp-rodent_exclusion', 'glyp-route_change', 'glyp-route_optimization', 'glyp-rows', 'glyp-ruler', 'glyp-sales', 'glyp-sales_contest', 'glyp-sales_pipeline', 'glyp-sales_tax', 'glyp-sales_tax_exemption', 'glyp-schedule_lock', 'glyp-schedule_lock_afternoon', 'glyp-schedule_lock_day', 'glyp-schedule_lock_exact', 'glyp-schedule_lock_four_hour', 'glyp-schedule_lock_morning', 'glyp-schedule_lock_none', 'glyp-schedule_lock_two_day', 'glyp-schedule_lock_two_hour', 'glyp-schedule_lock_week', 'glyp-schedule_tyoe_every', 'glyp-schedule_type_every', 'glyp-schedule_type_none', 'glyp-schedule_type_on_demand', 'glyp-schedule_type_schedule', 'glyp-scheduled', 'glyp-scheduling', 'glyp-school_church', 'glyp-script', 'glyp-search', 'glyp-seeding', 'glyp-segment', 'glyp-sent', 'glyp-sentricon', 'glyp-service_agreement', 'glyp-service_form', 'glyp-service_miscellaneous', 'glyp-service_reminder', 'glyp-service_report', 'glyp-service_request', 'glyp-services', 'glyp-settings', 'glyp-setup', 'glyp-signature', 'glyp-simulator', 'glyp-site_map', 'glyp-site_map_annotation', 'glyp-site_map_chemical', 'glyp-site_map_edit_details', 'glyp-site_map_edit_geo', 'glyp-site_map_equipment', 'glyp-site_map_exterior', 'glyp-site_map_foundation', 'glyp-site_map_icon', 'glyp-site_map_interior', 'glyp-site_map_label', 'glyp-site_map_perimeter', 'glyp-site_map_settings_left', 'glyp-site_map_settings_right', 'glyp-site_map_template', 'glyp-skip', 'glyp-smart_traps', 'glyp-sms', 'glyp-snow_removal', 'glyp-sort', 'glyp-special', 'glyp-split', 'glyp-spp_scorecard', 'glyp-square_edge', 'glyp-start_sheet', 'glyp-start_time', 'glyp-statement', 'glyp-states', 'glyp-sticky', 'glyp-stop_time', 'glyp-store_material', 'glyp-store_order', 'glyp-store_order_back_order', 'glyp-store_order_cancelled', 'glyp-store_order_complete', 'glyp-store_order_pending', 'glyp-store_order_pending_return', 'glyp-store_order_pre_order', 'glyp-store_order_returned', 'glyp-stores', 'glyp-styling', 'glyp-subscribe', 'glyp-supplemental', 'glyp-support', 'glyp-sync', 'glyp-table', 'glyp-tablet', 'glyp-tablets', 'glyp-tag_manager', 'glyp-tags', 'glyp-task_runs', 'glyp-tech_pest_sightings', 'glyp-technician', 'glyp-technician_approach', 'glyp-template', 'glyp-termite_fumigation', 'glyp-terrier', 'glyp-territory', 'glyp-text', 'glyp-thermometer', 'glyp-third_party_billing', 'glyp-ticket_type', 'glyp-tickets', 'glyp-tidy', 'glyp-time', 'glyp-time_entry', 'glyp-timeline', 'glyp-toolbox', 'glyp-tqi', 'glyp-tracker', 'glyp-tracking', 'glyp-training_course', 'glyp-treatment_split', 'glyp-trends', 'glyp-trip_charge', 'glyp-ui_type', 'glyp-unchecked', 'glyp-undo', 'glyp-unit_sweep', 'glyp-unit_tag', 'glyp-university', 'glyp-unlocked', 'glyp-unscheduled', 'glyp-update_check_positions', 'glyp-upload', 'glyp-user', 'glyp-user_credit', 'glyp-user_tags', 'glyp-users', 'glyp-utility', 'glyp-vacation', 'glyp-vacuum', 'glyp-variant', 'glyp-vendor', 'glyp-versions', 'glyp-video', 'glyp-visible', 'glyp-warehouse', 'glyp-wdo', 'glyp-web_dusting', 'glyp-website', 'glyp-wildlife', 'glyp-wildlife_exclusion', 'glyp-winter_check', 'glyp-wizard', 'glyp-work', 'glyp-work_class', 'glyp-work_code', 'glyp-work_order', 'glyp-work_production', 'glyp-work_progress', 'glyp-work_yield', 'glyp-year', 'glyp-yield', 'glyp-zip_code', 'glyp-zone_adjacent', 'glyp-zone_check', 'glyp-zone_production', 'glyp-zone_remote', 'glyp-zoom_fit'] as const
|
|
3
|
+
const names = ['glyp-abacus', 'glyp-account', 'glyp-accounts_payable', 'glyp-accounts_receivable', 'glyp-action_log', 'glyp-activate', 'glyp-active', 'glyp-activity_high', 'glyp-activity_low', 'glyp-activity_medium', 'glyp-activity_none', 'glyp-address1', 'glyp-adjustment', 'glyp-admin', 'glyp-administration', 'glyp-air_freshener', 'glyp-alert', 'glyp-align_horizontal', 'glyp-align_vertical', 'glyp-anchor', 'glyp-announcement', 'glyp-app_update', 'glyp-append_selection', 'glyp-appointment', 'glyp-appointment_setup', 'glyp-archive', 'glyp-arrow_down', 'glyp-arrow_left', 'glyp-arrow_right', 'glyp-arrow_transfer', 'glyp-arrow_up', 'glyp-ascending', 'glyp-attachment', 'glyp-audit_analyzer', 'glyp-autopay', 'glyp-availability', 'glyp-background_batch', 'glyp-background_job', 'glyp-bait_for_demolition', 'glyp-bar_chart', 'glyp-barcode', 'glyp-bat_exclusion', 'glyp-bed_bug_fumigation', 'glyp-begin_service', 'glyp-belongs_to', 'glyp-billing', 'glyp-billing_office', 'glyp-billing_terms', 'glyp-billto', 'glyp-bioremediation', 'glyp-bird_exclusion', 'glyp-black_light', 'glyp-branch', 'glyp-branch_bats', 'glyp-branch_birds', 'glyp-branch_general', 'glyp-branch_irrigation', 'glyp-branch_landscape', 'glyp-branch_multi_housing', 'glyp-branch_office', 'glyp-branch_orders', 'glyp-branch_residential', 'glyp-branch_sales', 'glyp-branch_termites', 'glyp-branch_weeds', 'glyp-branch_wildlife', 'glyp-build', 'glyp-calculate', 'glyp-calendar', 'glyp-calendar_branch', 'glyp-calendar_map', 'glyp-calendar_rolling', 'glyp-calendar_share', 'glyp-calendar_snap', 'glyp-california', 'glyp-call', 'glyp-callback', 'glyp-camera', 'glyp-cancellation', 'glyp-cancelled', 'glyp-card_american_express', 'glyp-card_discover', 'glyp-card_mastercard', 'glyp-card_visa', 'glyp-catalog', 'glyp-caught', 'glyp-cert', 'glyp-check_all', 'glyp-check_in', 'glyp-check_in_service', 'glyp-checked', 'glyp-checked_empty', 'glyp-checkmark', 'glyp-checkmark_filled', 'glyp-checkout', 'glyp-chemical', 'glyp-chemical_authorization', 'glyp-chemical_backpack_sprayer', 'glyp-chemical_biological_controls', 'glyp-chemical_disinfectant', 'glyp-chemical_fertilizer', 'glyp-chemical_flying_insect_bait', 'glyp-chemical_fumigants', 'glyp-chemical_insect_aerosol', 'glyp-chemical_insect_bait', 'glyp-chemical_insect_dust', 'glyp-chemical_insect_granules', 'glyp-chemical_insect_spray', 'glyp-chemical_label', 'glyp-chemical_mole_bait', 'glyp-chemical_pest_bird_control', 'glyp-chemical_pheromone_products', 'glyp-chemical_power_sprayer', 'glyp-chemical_rodenticide_block', 'glyp-chemical_rodenticide_non_block', 'glyp-chemical_sds', 'glyp-chemical_tree_products', 'glyp-chemical_weed_control', 'glyp-chemical_wildlife_repellents', 'glyp-chemical_wildlife_toxicants', 'glyp-chevron_down', 'glyp-chevron_left', 'glyp-chevron_right', 'glyp-chevron_up', 'glyp-city', 'glyp-click', 'glyp-client_actions', 'glyp-client_pest_sightings', 'glyp-client_required', 'glyp-close', 'glyp-cloud', 'glyp-clypboard', 'glyp-clypedia', 'glyp-clypmart', 'glyp-code', 'glyp-code_details', 'glyp-collections', 'glyp-columns', 'glyp-comment', 'glyp-comment_filled', 'glyp-commission_origin', 'glyp-commissions', 'glyp-company_setup', 'glyp-compass', 'glyp-complete', 'glyp-complete_certificate', 'glyp-complete_enrollment', 'glyp-condition_appliance_machinery', 'glyp-condition_customer', 'glyp-condition_device', 'glyp-condition_door_window', 'glyp-condition_dumpster', 'glyp-condition_entry_point', 'glyp-condition_evidence', 'glyp-condition_exclusion', 'glyp-condition_exterior', 'glyp-condition_interior', 'glyp-condition_plumbing', 'glyp-condition_prep_storage', 'glyp-condition_rodent_evidence', 'glyp-condition_rodent_exclusion', 'glyp-condition_roof_ceiling', 'glyp-condition_structure', 'glyp-condition_supplies', 'glyp-condition_termites', 'glyp-condition_ventilation', 'glyp-condition_wall_floor', 'glyp-condition_wildlife', 'glyp-conditions', 'glyp-consolidate', 'glyp-construction', 'glyp-contract', 'glyp-contract_cancellation', 'glyp-contract_overview', 'glyp-conversation', 'glyp-copesan', 'glyp-copy', 'glyp-credit_memo', 'glyp-credit_non_revenue', 'glyp-credit_production', 'glyp-credit_prospect', 'glyp-credit_sales_bonus', 'glyp-crew', 'glyp-cumulative', 'glyp-cursor', 'glyp-custom_form', 'glyp-customer', 'glyp-customer_service', 'glyp-dashboard', 'glyp-data_dive', 'glyp-data_dive_query', 'glyp-data_dives', 'glyp-database', 'glyp-dead_animal_removal', 'glyp-default', 'glyp-delete', 'glyp-demo_mode', 'glyp-descending', 'glyp-design', 'glyp-desktop', 'glyp-detail_report', 'glyp-developer', 'glyp-device', 'glyp-device_sync', 'glyp-differential', 'glyp-disable', 'glyp-disabled_filled', 'glyp-distribute_horizontal', 'glyp-distribute_vertical', 'glyp-division', 'glyp-document', 'glyp-documentation', 'glyp-documents', 'glyp-download', 'glyp-driving', 'glyp-duplicate', 'glyp-duplicate_location', 'glyp-duration', 'glyp-edit', 'glyp-email', 'glyp-employment', 'glyp-entertainment_public_venues', 'glyp-equipment_bear_trap', 'glyp-equipment_flying_insect_bait_station', 'glyp-equipment_flying_insect_light_trap', 'glyp-equipment_insect_monitor', 'glyp-equipment_lethal_animal_trap', 'glyp-equipment_live_animal_trap', 'glyp-equipment_live_rodent_trap', 'glyp-equipment_maintenance', 'glyp-equipment_map_viewer', 'glyp-equipment_maps', 'glyp-equipment_mosquito_trap', 'glyp-equipment_other', 'glyp-equipment_pheromone_trap', 'glyp-equipment_pick_up', 'glyp-equipment_rodent_bait', 'glyp-equipment_rodent_trap', 'glyp-equipment_termite_bait_station', 'glyp-equipment_termite_monitor', 'glyp-exclusion', 'glyp-expand', 'glyp-expiring', 'glyp-exterior', 'glyp-extras', 'glyp-facebook', 'glyp-farm', 'glyp-farm_grain_seed', 'glyp-fast_and_frequent', 'glyp-favorite', 'glyp-feedback', 'glyp-field_guide', 'glyp-field_training', 'glyp-file_blank', 'glyp-file_html', 'glyp-file_image', 'glyp-file_pdf', 'glyp-file_printable', 'glyp-file_spreadsheet', 'glyp-file_text', 'glyp-filter', 'glyp-finance', 'glyp-finding', 'glyp-folder', 'glyp-followup', 'glyp-food_bev_pharma', 'glyp-formula', 'glyp-fuel', 'glyp-fumigation', 'glyp-function', 'glyp-function_aggregate', 'glyp-function_time', 'glyp-gain_loss', 'glyp-generate_invoices', 'glyp-generate_orders', 'glyp-geo', 'glyp-geo_heat_map', 'glyp-google', 'glyp-google_calendar', 'glyp-government', 'glyp-grain_seed', 'glyp-grid', 'glyp-grouped', 'glyp-grouping_combine', 'glyp-grouping_separate', 'glyp-has_many', 'glyp-health_care', 'glyp-heat_map', 'glyp-heat_treatment', 'glyp-help', 'glyp-hidden', 'glyp-hint', 'glyp-honeybee_removal', 'glyp-housing', 'glyp-in_progress', 'glyp-incomplete_certificate', 'glyp-industrial', 'glyp-info', 'glyp-information_technology', 'glyp-inspect_map', 'glyp-inspections', 'glyp-insulation', 'glyp-interior', 'glyp-invoice', 'glyp-invoice_review', 'glyp-irrigation_install', 'glyp-irrigation_maintenance', 'glyp-items', 'glyp-job', 'glyp-join', 'glyp-join_inner', 'glyp-join_left', 'glyp-justice', 'glyp-k9_inspection', 'glyp-key', 'glyp-label_bottom', 'glyp-label_left', 'glyp-label_right', 'glyp-label_top', 'glyp-labor', 'glyp-landscape', 'glyp-landscape_maintenance', 'glyp-laptop', 'glyp-lead', 'glyp-lead_listing', 'glyp-lead_source', 'glyp-leads_report', 'glyp-learning_hub', 'glyp-ledger', 'glyp-legal', 'glyp-license', 'glyp-lifetime', 'glyp-line_cap_arrow', 'glyp-line_cap_bar', 'glyp-line_cap_circle', 'glyp-line_cap_none', 'glyp-line_caps', 'glyp-line_style', 'glyp-link', 'glyp-location', 'glyp-location_charge', 'glyp-location_credit', 'glyp-location_kind', 'glyp-location_message', 'glyp-location_origin', 'glyp-location_tags', 'glyp-locations', 'glyp-locked', 'glyp-lodging', 'glyp-log_in', 'glyp-log_out', 'glyp-log_report', 'glyp-logbook', 'glyp-logbook_documents', 'glyp-lot_number', 'glyp-manager', 'glyp-map', 'glyp-markdown', 'glyp-market', 'glyp-materials', 'glyp-mattress_encasement', 'glyp-merge', 'glyp-message_billing', 'glyp-message_collections', 'glyp-message_complaint', 'glyp-message_misc', 'glyp-message_technician', 'glyp-messages', 'glyp-misc', 'glyp-miscellaneous', 'glyp-move_order', 'glyp-mowing', 'glyp-multi_housing', 'glyp-mute', 'glyp-navicon', 'glyp-new_location', 'glyp-no_charge', 'glyp-no_service', 'glyp-note', 'glyp-note_access', 'glyp-note_billing', 'glyp-note_collection', 'glyp-note_complaint', 'glyp-note_directions', 'glyp-note_focused', 'glyp-note_office', 'glyp-note_preservice', 'glyp-note_sales', 'glyp-note_service', 'glyp-notification', 'glyp-number', 'glyp-office', 'glyp-office_government', 'glyp-office_training', 'glyp-on_demand', 'glyp-on_demand_order', 'glyp-open', 'glyp-open_invoice', 'glyp-operations', 'glyp-options', 'glyp-order_actions', 'glyp-order_approved', 'glyp-order_batch', 'glyp-order_editor', 'glyp-order_pending', 'glyp-order_series', 'glyp-order_unapproved', 'glyp-org_structure', 'glyp-org_unit', 'glyp-org_unit_settings', 'glyp-origin', 'glyp-origin_client', 'glyp-origin_tech', 'glyp-outlook', 'glyp-overlap', 'glyp-password', 'glyp-past_due', 'glyp-paused', 'glyp-pay_brackets', 'glyp-payment', 'glyp-payment_ach', 'glyp-payment_application_apply', 'glyp-payment_application_correction', 'glyp-payment_application_initial', 'glyp-payment_application_refund', 'glyp-payment_application_transfer', 'glyp-payment_application_unapply', 'glyp-payment_application_unapply_all', 'glyp-payment_applications', 'glyp-payment_batch', 'glyp-payment_cash', 'glyp-payment_check', 'glyp-payment_coupon', 'glyp-payment_credit', 'glyp-payment_discount', 'glyp-payment_eft', 'glyp-payment_finance_charge', 'glyp-payments', 'glyp-payroll', 'glyp-pdf', 'glyp-pending', 'glyp-periodic', 'glyp-periodic_assessment', 'glyp-permission', 'glyp-pest', 'glyp-pest_ants', 'glyp-pest_bats', 'glyp-pest_bed_bugs', 'glyp-pest_birds', 'glyp-pest_crawling_insects', 'glyp-pest_dermestids', 'glyp-pest_fall_invaders', 'glyp-pest_flying_insects', 'glyp-pest_moles', 'glyp-pest_mosquitoes', 'glyp-pest_nuisance_animals', 'glyp-pest_ornamental', 'glyp-pest_ornamental_diseases', 'glyp-pest_roaches', 'glyp-pest_rodents', 'glyp-pest_scorpions', 'glyp-pest_snakes', 'glyp-pest_spiders', 'glyp-pest_stinging_insects', 'glyp-pest_stored_product_pests', 'glyp-pest_ticks_and_fleas', 'glyp-pest_viruses_and_bacteria', 'glyp-pest_weeds', 'glyp-pest_wood_destroyers', 'glyp-phone', 'glyp-pick_date', 'glyp-pick_list', 'glyp-platform_android', 'glyp-platform_ios', 'glyp-platform_macos', 'glyp-platform_windows', 'glyp-play', 'glyp-plus', 'glyp-plus_filled', 'glyp-plus_outline', 'glyp-portal', 'glyp-price_increase', 'glyp-price_increase_alt', 'glyp-pricing_table', 'glyp-print', 'glyp-privacy', 'glyp-product_sale', 'glyp-production', 'glyp-professional_consultation', 'glyp-program', 'glyp-program_elements', 'glyp-program_initiation', 'glyp-program_order', 'glyp-program_review', 'glyp-promo', 'glyp-proposal', 'glyp-protection', 'glyp-purchase_order', 'glyp-quality', 'glyp-query', 'glyp-radio_checked', 'glyp-radio_empty', 'glyp-reassign', 'glyp-receipt', 'glyp-recent', 'glyp-recommendation', 'glyp-record_details', 'glyp-recurring_revenue', 'glyp-redo', 'glyp-refresh', 'glyp-refund', 'glyp-region', 'glyp-released', 'glyp-remove', 'glyp-renewal', 'glyp-report', 'glyp-required_input', 'glyp-reschedule', 'glyp-restaurant_bar', 'glyp-revenue', 'glyp-review', 'glyp-rfid', 'glyp-ride_along', 'glyp-rodent_exclusion', 'glyp-route_change', 'glyp-route_optimization', 'glyp-rows', 'glyp-ruler', 'glyp-sales', 'glyp-sales_contest', 'glyp-sales_pipeline', 'glyp-sales_tax', 'glyp-sales_tax_exemption', 'glyp-schedule_lock', 'glyp-schedule_lock_afternoon', 'glyp-schedule_lock_day', 'glyp-schedule_lock_exact', 'glyp-schedule_lock_four_hour', 'glyp-schedule_lock_morning', 'glyp-schedule_lock_none', 'glyp-schedule_lock_two_day', 'glyp-schedule_lock_two_hour', 'glyp-schedule_lock_week', 'glyp-schedule_tyoe_every', 'glyp-schedule_type_every', 'glyp-schedule_type_none', 'glyp-schedule_type_on_demand', 'glyp-schedule_type_schedule', 'glyp-scheduled', 'glyp-scheduling', 'glyp-school_church', 'glyp-script', 'glyp-search', 'glyp-seeding', 'glyp-segment', 'glyp-sent', 'glyp-sentricon', 'glyp-service_agreement', 'glyp-service_form', 'glyp-service_miscellaneous', 'glyp-service_reminder', 'glyp-service_report', 'glyp-service_request', 'glyp-services', 'glyp-settings', 'glyp-setup', 'glyp-signature', 'glyp-simulator', 'glyp-site_map', 'glyp-site_map_annotation', 'glyp-site_map_chemical', 'glyp-site_map_edit_details', 'glyp-site_map_edit_geo', 'glyp-site_map_equipment', 'glyp-site_map_exterior', 'glyp-site_map_foundation', 'glyp-site_map_icon', 'glyp-site_map_interior', 'glyp-site_map_label', 'glyp-site_map_perimeter', 'glyp-site_map_settings_left', 'glyp-site_map_settings_right', 'glyp-site_map_template', 'glyp-skip', 'glyp-smart_traps', 'glyp-sms', 'glyp-snow_removal', 'glyp-sort', 'glyp-special', 'glyp-split', 'glyp-spp_scorecard', 'glyp-square_edge', 'glyp-start_sheet', 'glyp-start_time', 'glyp-statement', 'glyp-states', 'glyp-sticky', 'glyp-stop_time', 'glyp-store_material', 'glyp-store_order', 'glyp-store_order_back_order', 'glyp-store_order_cancelled', 'glyp-store_order_complete', 'glyp-store_order_pending', 'glyp-store_order_pending_return', 'glyp-store_order_pre_order', 'glyp-store_order_returned', 'glyp-stores', 'glyp-styling', 'glyp-subscribe', 'glyp-supplemental', 'glyp-support', 'glyp-sync', 'glyp-table', 'glyp-tablet', 'glyp-tablets', 'glyp-tag_manager', 'glyp-tags', 'glyp-task_runs', 'glyp-tech_pest_sightings', 'glyp-technician', 'glyp-technician_approach', 'glyp-template', 'glyp-termite_fumigation', 'glyp-terrier', 'glyp-territory', 'glyp-text', 'glyp-thermometer', 'glyp-third_party_billing', 'glyp-ticket_type', 'glyp-tickets', 'glyp-tidy', 'glyp-time', 'glyp-time_entry', 'glyp-timeline', 'glyp-toolbox', 'glyp-tqi', 'glyp-tracker', 'glyp-tracking', 'glyp-training_course', 'glyp-treatment_split', 'glyp-trends', 'glyp-trip_charge', 'glyp-ui_type', 'glyp-unchecked', 'glyp-undo', 'glyp-unit_sweep', 'glyp-unit_tag', 'glyp-university', 'glyp-unlocked', 'glyp-unscheduled', 'glyp-update_check_positions', 'glyp-upload', 'glyp-user', 'glyp-user_credit', 'glyp-user_tags', 'glyp-users', 'glyp-utility', 'glyp-vacation', 'glyp-vacuum', 'glyp-variant', 'glyp-vendor', 'glyp-versions', 'glyp-video', 'glyp-visible', 'glyp-warehouse', 'glyp-wdo', 'glyp-web_dusting', 'glyp-website', 'glyp-wildlife', 'glyp-wildlife_exclusion', 'glyp-winter_check', 'glyp-wizard', 'glyp-work', 'glyp-work_class', 'glyp-work_code', 'glyp-work_order', 'glyp-work_production', 'glyp-work_progress', 'glyp-work_yield', 'glyp-year', 'glyp-yield', 'glyp-zip_code', 'glyp-zone_adjacent', 'glyp-zone_check', 'glyp-zone_production', 'glyp-zone_remote', 'glyp-zoom_fit'] as const
|
|
4
4
|
|
|
5
5
|
const Glyps = {
|
|
6
6
|
names
|
package/terrier/overlays.ts
CHANGED
|
@@ -273,24 +273,40 @@ function anchorBox(size: Size, anchor: Box, container: Size, options: AnchorOpti
|
|
|
273
273
|
return preferredResult
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
+
|
|
276
277
|
/**
|
|
277
|
-
*
|
|
278
|
-
* @param elem
|
|
279
|
-
* @param anchor the anchor element
|
|
278
|
+
* Gets the size of an element, forcing the browser to calculate it if necessary.
|
|
279
|
+
* @param elem
|
|
280
280
|
*/
|
|
281
|
-
function
|
|
281
|
+
function getElementSize(elem: HTMLElement): Size {
|
|
282
282
|
// Sometimes the actual width and height of the rendered element is incorrect before we set the style attribute.
|
|
283
283
|
// Setting the style attribute first forces the browser to re-calculate the size of the element so that we can use
|
|
284
284
|
// the "real" size to calculate where to anchor the element.
|
|
285
285
|
elem.setAttribute('style', 'top:0;left:0;')
|
|
286
286
|
|
|
287
|
-
|
|
287
|
+
return {
|
|
288
288
|
width: elem.offsetWidth,
|
|
289
289
|
height: elem.offsetHeight
|
|
290
290
|
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Gets the current size of the browser window.
|
|
295
|
+
*/
|
|
296
|
+
function getWindowSize(): Size {
|
|
297
|
+
return {width: window.innerWidth, height: window.innerHeight}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Anchors one element to the side of another.
|
|
302
|
+
* @param elem the element to reposition
|
|
303
|
+
* @param anchor the anchor element
|
|
304
|
+
*/
|
|
305
|
+
function anchorElement(elem: HTMLElement, anchor: HTMLElement) {
|
|
306
|
+
const elemSize = getElementSize(elem)
|
|
291
307
|
log.debug(`Anchoring element`, elem, anchor)
|
|
292
308
|
const rect = anchor.getBoundingClientRect()
|
|
293
|
-
const win =
|
|
309
|
+
const win = getWindowSize()
|
|
294
310
|
const anchorResult = anchorBox(elemSize, rect, win, {preferredSide: 'bottom'})
|
|
295
311
|
|
|
296
312
|
let styleString = ""
|
|
@@ -302,6 +318,25 @@ function anchorElement(elem: HTMLElement, anchor: HTMLElement) {
|
|
|
302
318
|
elem.setAttribute('style', styleString)
|
|
303
319
|
}
|
|
304
320
|
|
|
321
|
+
/**
|
|
322
|
+
* Centers an element on the page.
|
|
323
|
+
* @param elem
|
|
324
|
+
*/
|
|
325
|
+
function centerElement(elem: HTMLElement) {
|
|
326
|
+
const elemSize = getElementSize(elem)
|
|
327
|
+
const win = getWindowSize()
|
|
328
|
+
log.debug(`Centering element`, elem, win)
|
|
329
|
+
const cappedSize = {
|
|
330
|
+
width: Math.min(win.width, elemSize.width),
|
|
331
|
+
height: Math.min(win.height, elemSize.height)
|
|
332
|
+
}
|
|
333
|
+
const styleString = [
|
|
334
|
+
`left: ${(win.width - cappedSize.width)/2}px`,
|
|
335
|
+
`top: ${(win.height - cappedSize.height)/2}px`
|
|
336
|
+
].join('; ')
|
|
337
|
+
elem.setAttribute('style', styleString)
|
|
338
|
+
}
|
|
339
|
+
|
|
305
340
|
|
|
306
341
|
////////////////////////////////////////////////////////////////////////////////
|
|
307
342
|
// Export
|
|
@@ -309,6 +344,7 @@ function anchorElement(elem: HTMLElement, anchor: HTMLElement) {
|
|
|
309
344
|
|
|
310
345
|
const Overlays = {
|
|
311
346
|
anchorElement,
|
|
347
|
+
centerElement,
|
|
312
348
|
anchorBox
|
|
313
349
|
}
|
|
314
350
|
|
|
@@ -154,13 +154,12 @@ export default abstract class ContentPart<TState> extends TerrierPart<TState> {
|
|
|
154
154
|
constructor: { new(p: PartParent, id: string, state: DropdownStateType): DropdownType; },
|
|
155
155
|
state: DropdownStateType,
|
|
156
156
|
target: EventTarget | null) {
|
|
157
|
-
if (!(target && target instanceof HTMLElement)) {
|
|
158
|
-
throw "Trying to show a dropdown without an element target!"
|
|
159
|
-
}
|
|
160
157
|
const dropdown = this.app.addOverlay(constructor, state, 'dropdown')
|
|
161
158
|
dropdown.parentPart = this
|
|
162
|
-
|
|
163
|
-
|
|
159
|
+
if (target && target instanceof HTMLElement) {
|
|
160
|
+
dropdown.anchor(target)
|
|
161
|
+
this.app.lastDropdownTarget = target
|
|
162
|
+
}
|
|
164
163
|
}
|
|
165
164
|
|
|
166
165
|
clearDropdowns() {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {Action, RenderActionOptions} from "../theme"
|
|
1
|
+
import {Action, IconName, RenderActionOptions} from "../theme"
|
|
2
2
|
import ContentPart, {ActionLevel} from "./content-part"
|
|
3
3
|
import {PartTag} from "tuff-core/parts"
|
|
4
4
|
import {optionsForSelect, SelectOptions} from "tuff-core/forms"
|
|
5
5
|
import {UntypedKey} from "tuff-core/messages"
|
|
6
6
|
import {Logger} from "tuff-core/logging"
|
|
7
|
-
import {HtmlParentTag} from "tuff-core/html"
|
|
8
7
|
|
|
9
8
|
const log = new Logger("Terrier PagePart")
|
|
10
9
|
|
|
@@ -18,15 +17,17 @@ export type ContentWidth = "normal" | "wide"
|
|
|
18
17
|
type BaseFieldDef = { name: string } & ToolbarFieldDefOptions
|
|
19
18
|
|
|
20
19
|
type ToolbarFieldDefOptions = {
|
|
21
|
-
onChangeKey?: UntypedKey
|
|
22
|
-
onInputKey?: UntypedKey
|
|
23
|
-
defaultValue?: string
|
|
20
|
+
onChangeKey?: UntypedKey
|
|
21
|
+
onInputKey?: UntypedKey
|
|
22
|
+
defaultValue?: string
|
|
24
23
|
tooltip?: string
|
|
24
|
+
title?: string
|
|
25
|
+
icon?: IconName
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
type ToolbarSelectDef = { type: 'select', options: SelectOptions } & BaseFieldDef
|
|
28
29
|
|
|
29
|
-
type ValuedInputType = 'text' | 'color' | 'date' | 'datetime-local' | 'email' | 'hidden' | 'month' | 'number' | 'password' | 'search' | 'tel' | 'time' | 'url' | 'week'
|
|
30
|
+
type ValuedInputType = 'text' | 'color' | 'date' | 'datetime-local' | 'email' | 'hidden' | 'month' | 'number' | 'password' | 'search' | 'tel' | 'time' | 'url' | 'week' | 'checkbox'
|
|
30
31
|
type ToolbarValuedInputDef = { type: ValuedInputType } & BaseFieldDef
|
|
31
32
|
|
|
32
33
|
/**
|
|
@@ -163,7 +164,7 @@ export default abstract class PagePart<TState> extends ContentPart<TState> {
|
|
|
163
164
|
}
|
|
164
165
|
|
|
165
166
|
protected renderToolbarFields(parent: PartTag) {
|
|
166
|
-
parent.div('.fields
|
|
167
|
+
parent.div('.fields', fields => {
|
|
167
168
|
for (const name of this._toolbarFieldsOrder) {
|
|
168
169
|
const def = this._toolbarFields[name]
|
|
169
170
|
if (!def) {
|
|
@@ -171,18 +172,33 @@ export default abstract class PagePart<TState> extends ContentPart<TState> {
|
|
|
171
172
|
continue;
|
|
172
173
|
}
|
|
173
174
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
175
|
+
fields.label(label => {
|
|
176
|
+
if (def.icon?.length) {
|
|
177
|
+
label.i('.icon').class(def.icon)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (def.title?.length) {
|
|
181
|
+
label.div('.title').text(def.title)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (def.type === 'select') {
|
|
185
|
+
const select = label.select({name: def.name}, select => {
|
|
186
|
+
optionsForSelect(select, def.options, def.defaultValue)
|
|
187
|
+
})
|
|
188
|
+
if (def.onChangeKey) select.emitChange(def.onChangeKey)
|
|
189
|
+
if (def.onInputKey) select.emitInput(def.onInputKey)
|
|
190
|
+
} else {
|
|
191
|
+
const input = label.input({name: def.name, type: def.type, value: def.defaultValue})
|
|
192
|
+
if (def.type == 'checkbox' && def.defaultValue?.length) {
|
|
193
|
+
input.attrs({checked: def.defaultValue == 'true'})
|
|
194
|
+
}
|
|
195
|
+
if (def.onChangeKey) input.emitChange(def.onChangeKey)
|
|
196
|
+
if (def.onInputKey) input.emitInput(def.onInputKey)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
if (def.tooltip?.length) label.dataAttr('tooltip', def.tooltip)
|
|
201
|
+
})
|
|
186
202
|
}
|
|
187
203
|
})
|
|
188
204
|
}
|
package/terrier/schema.ts
CHANGED
|
@@ -8,7 +8,7 @@ import inflection from "inflection"
|
|
|
8
8
|
/**
|
|
9
9
|
* Possible visibility for models and columns.
|
|
10
10
|
*/
|
|
11
|
-
export type MetaVisibility = 'common' | 'uncommon' | '
|
|
11
|
+
export type MetaVisibility = 'common' | 'uncommon' | 'hidden'
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Definition for a single column in the schema.
|
|
@@ -87,12 +87,13 @@ async function get(): Promise<SchemaDef> {
|
|
|
87
87
|
* @param belongsTo
|
|
88
88
|
*/
|
|
89
89
|
function belongsToDisplay(belongsTo: BelongsToDef): string {
|
|
90
|
+
const btName = inflection.titleize(belongsTo.name)
|
|
90
91
|
if (belongsTo.name != inflection.singularize(inflection.tableize(belongsTo.model))) {
|
|
91
92
|
// the model is different than the name of the association
|
|
92
|
-
return `${
|
|
93
|
+
return `${btName} (${belongsTo.model})`
|
|
93
94
|
}
|
|
94
95
|
else {
|
|
95
|
-
return
|
|
96
|
+
return btName
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
|
package/terrier/tabs.ts
CHANGED
|
@@ -117,17 +117,17 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
_beforeActions: Action[] = []
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
this.
|
|
122
|
+
addBeforeAction(action: Action) {
|
|
123
|
+
this._beforeActions.push(action)
|
|
124
124
|
this.dirty()
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
_afterActions: Action[] = []
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
this.
|
|
129
|
+
addAfterAction(action: Action) {
|
|
130
|
+
this._afterActions.push(action)
|
|
131
131
|
this.dirty()
|
|
132
132
|
}
|
|
133
133
|
|
|
@@ -141,8 +141,8 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
|
|
|
141
141
|
}
|
|
142
142
|
parent.div('tt-tab-container', this.state.side, container => {
|
|
143
143
|
container.div('.tt-flex.tt-tab-list', tabList => {
|
|
144
|
-
if (this.
|
|
145
|
-
this.theme.renderActions(tabList,
|
|
144
|
+
if (this._beforeActions.length) {
|
|
145
|
+
this.theme.renderActions(tabList, this._beforeActions, {defaultClass: 'action'})
|
|
146
146
|
}
|
|
147
147
|
for (const tab of Object.values(this.tabs)) {
|
|
148
148
|
if (tab.state == 'hidden') continue
|
|
@@ -162,9 +162,9 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
|
|
|
162
162
|
}
|
|
163
163
|
})
|
|
164
164
|
}
|
|
165
|
-
if (this.
|
|
165
|
+
if (this._afterActions.length) {
|
|
166
166
|
tabList.div('.spacer')
|
|
167
|
-
this.theme.renderActions(tabList,
|
|
167
|
+
this.theme.renderActions(tabList, this._afterActions, {defaultClass: 'action'})
|
|
168
168
|
}
|
|
169
169
|
})
|
|
170
170
|
|
package/terrier/theme.ts
CHANGED
|
@@ -31,6 +31,7 @@ export type Packet = {
|
|
|
31
31
|
*/
|
|
32
32
|
export type Action = {
|
|
33
33
|
title?: string
|
|
34
|
+
subtitle?: string
|
|
34
35
|
tooltip?: string
|
|
35
36
|
icon?: IconName
|
|
36
37
|
href?: string
|
|
@@ -98,6 +99,9 @@ export default class Theme {
|
|
|
98
99
|
if (action.title?.length) {
|
|
99
100
|
a.div('.title', {text: action.title})
|
|
100
101
|
}
|
|
102
|
+
if (action.subtitle?.length) {
|
|
103
|
+
a.div('.subtitle', {text: action.subtitle})
|
|
104
|
+
}
|
|
101
105
|
else {
|
|
102
106
|
a.class('icon-only')
|
|
103
107
|
}
|