terrier-engine 4.8.2 → 4.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data-dive/queries/columns.ts +1 -1
- package/package.json +1 -1
- package/terrier/gen/badges.ts +7 -5
- package/terrier/glyps.ts +2 -3
- package/terrier/images/badges/badge-accounts_payable-handling.svg +12 -12
- package/terrier/images/badges/badge-accounts_receivable-handling.svg +12 -12
- package/terrier/images/badges/badge-applicator_license-admin.svg +14 -14
- package/terrier/images/badges/badge-autopay-admin.svg +12 -12
- package/terrier/images/badges/badge-autopay-handling.svg +12 -12
- package/terrier/images/badges/badge-bats-admin.svg +12 -12
- package/terrier/images/badges/badge-bats-management.svg +12 -12
- package/terrier/images/badges/badge-bats-ops.svg +12 -12
- package/terrier/images/badges/badge-bats-sales.svg +12 -12
- package/terrier/images/badges/badge-clypboard_hardware-admin.svg +19 -19
- package/terrier/images/badges/badge-clypboard_project-admin.svg +14 -14
- package/terrier/images/badges/badge-clypmart-admin.svg +14 -14
- package/terrier/images/badges/badge-commercial-admin.svg +16 -16
- package/terrier/images/badges/badge-commercial-management.svg +16 -16
- package/terrier/images/badges/badge-commercial-ops.svg +16 -16
- package/terrier/images/badges/badge-commercial-sales.svg +16 -16
- package/terrier/images/badges/badge-compliance-admin.svg +12 -12
- package/terrier/images/badges/badge-contracts-admin.svg +14 -14
- package/terrier/images/badges/badge-contracts-reporting.svg +14 -14
- package/terrier/images/badges/badge-copesan-admin.svg +12 -12
- package/terrier/images/badges/badge-credit_card-admin.svg +16 -16
- package/terrier/images/badges/badge-customer_call-handling.svg +12 -12
- package/terrier/images/badges/badge-customer_text-handling.svg +16 -16
- package/terrier/images/badges/badge-employee_data-admin.svg +12 -12
- package/terrier/images/badges/badge-employee_data-handling.svg +12 -12
- package/terrier/images/badges/badge-employee_data-reporting.svg +12 -12
- package/terrier/images/badges/badge-finance-admin.svg +12 -12
- package/terrier/images/badges/badge-finance-reporting.svg +12 -12
- package/terrier/images/badges/badge-fleet-admin.svg +17 -17
- package/terrier/images/badges/badge-fumigation-admin.svg +16 -16
- package/terrier/images/badges/badge-fumigation-management.svg +16 -16
- package/terrier/images/badges/badge-fumigation-ops.svg +16 -16
- package/terrier/images/badges/badge-information_technology-admin.svg +14 -14
- package/terrier/images/badges/badge-information_technology-management.svg +14 -14
- package/terrier/images/badges/badge-irrigation-admin.svg +14 -14
- package/terrier/images/badges/badge-irrigation-management.svg +14 -14
- package/terrier/images/badges/badge-irrigation-ops.svg +14 -14
- package/terrier/images/badges/badge-irrigation-sales.svg +14 -14
- package/terrier/images/badges/badge-landscape-admin.svg +12 -12
- package/terrier/images/badges/badge-landscape-management.svg +12 -12
- package/terrier/images/badges/badge-landscape-ops.svg +12 -12
- package/terrier/images/badges/badge-landscape-sales.svg +12 -12
- package/terrier/images/badges/badge-marketing-admin.svg +16 -16
- package/terrier/images/badges/badge-mergers_and_acquisitions-admin.svg +12 -12
- package/terrier/images/badges/badge-mergers_and_acquisitions-reporting.svg +12 -12
- package/terrier/images/badges/badge-multi_housing-admin.svg +16 -16
- package/terrier/images/badges/badge-multi_housing-management.svg +16 -16
- package/terrier/images/badges/badge-multi_housing-ops.svg +16 -16
- package/terrier/images/badges/badge-multi_housing-sales.svg +16 -16
- package/terrier/images/badges/badge-payroll-admin.svg +12 -12
- package/terrier/images/badges/badge-payroll-reporting.svg +12 -12
- package/terrier/images/badges/badge-pest-admin.svg +12 -12
- package/terrier/images/badges/badge-pest-management.svg +12 -12
- package/terrier/images/badges/badge-pest-ops.svg +12 -12
- package/terrier/images/badges/badge-pest-sales.svg +12 -12
- package/terrier/images/badges/badge-pro-admin.svg +16 -16
- package/terrier/images/badges/badge-pro-management.svg +16 -16
- package/terrier/images/badges/badge-pro-ops.svg +16 -16
- package/terrier/images/badges/badge-pro-sales.svg +16 -16
- package/terrier/images/badges/badge-program-admin.svg +12 -12
- package/terrier/images/badges/badge-proposals-handling.svg +14 -14
- package/terrier/images/badges/badge-residential-admin.svg +15 -15
- package/terrier/images/badges/badge-residential-management.svg +15 -15
- package/terrier/images/badges/badge-residential-ops.svg +15 -15
- package/terrier/images/badges/badge-residential-sales.svg +15 -15
- package/terrier/images/badges/badge-sales-management.svg +12 -12
- package/terrier/images/badges/badge-sales-reporting.svg +12 -12
- package/terrier/images/badges/badge-scripts-admin.svg +12 -12
- package/terrier/images/badges/badge-scripts-reporting.svg +12 -12
- package/terrier/images/badges/badge-termite-admin.svg +12 -12
- package/terrier/images/badges/badge-termite-management.svg +12 -12
- package/terrier/images/badges/badge-termite-ops.svg +12 -12
- package/terrier/images/badges/badge-termite-sales.svg +12 -12
- package/terrier/images/badges/badge-wildlife-admin.svg +12 -12
- package/terrier/images/badges/badge-wildlife-management.svg +12 -12
- package/terrier/images/badges/badge-wildlife-ops.svg +12 -12
- package/terrier/images/badges/badge-wildlife-sales.svg +12 -12
- package/terrier/list-viewer.ts +146 -0
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
<?xml version="1.0"?>
|
|
2
2
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32">
|
|
3
3
|
<defs>
|
|
4
|
-
<filter id="
|
|
4
|
+
<filter id="awildlife1" width="125%" height="126.9%" x="-12.5%" y="-9.6%" filterUnits="objectBoundingBox">
|
|
5
5
|
<feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/>
|
|
6
6
|
<feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="1"/>
|
|
7
7
|
<feColorMatrix in="shadowBlurOuter1" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.33 0"/>
|
|
8
8
|
</filter>
|
|
9
|
-
<path id="
|
|
9
|
+
<path id="bwildlife1" d="M20.069 2.97c1.45.179 2.655 1.04 3.414 2.388.354.629.605 1.37.724 2.172 1.222-.37 2.464-.253 3.548.372 1.265.73 2.038 1.995 2.21 3.532.142 1.276-.153 2.753-.93 4.098-.777 1.346-1.908 2.34-3.084 2.854a5.356 5.356 0 0 1-.88.299c.92 1.673 1.429 3.288 1.429 4.315 0 1.76-.784 3.21-1.96 4.255C23.268 28.386 21.53 29 20 29c-1.448 0-2.32-.368-3.126-.729l-.225-.106A1.464 1.464 0 0 0 16 28c-.262 0-.46.078-.649.165l-.225.106C14.32 28.632 13.448 29 12 29c-1.529 0-3.268-.614-4.54-1.745C6.284 26.211 5.5 24.76 5.5 23c0-1.027.508-2.642 1.428-4.317a5.171 5.171 0 0 1-.879-.297c-1.176-.514-2.307-1.508-3.084-2.854-.777-1.345-1.072-2.822-.93-4.098.172-1.537.945-2.801 2.21-3.532 1.084-.626 2.326-.742 3.55-.374.117-.8.368-1.541.722-2.17.759-1.347 1.964-2.21 3.414-2.388 1.45-.178 2.829.367 3.891 1.491.06.064.12.13.177.197.06-.067.119-.133.179-.197 1.062-1.124 2.44-1.669 3.89-1.49Z"/>
|
|
10
10
|
</defs>
|
|
11
11
|
<g fill="none" fill-rule="evenodd">
|
|
12
|
-
<use xlink:href="#
|
|
13
|
-
<use xlink:href="#
|
|
12
|
+
<use xlink:href="#bwildlife1" fill="#000" filter="url(#awildlife1)"/>
|
|
13
|
+
<use xlink:href="#bwildlife1" fill="#FFF"/>
|
|
14
14
|
<path fill="#76502D" fill-rule="nonzero" d="M16 14c4.5 0 8.5 6.5 8.5 9S22 27 20 27s-2.5-1-4-1-2 1-4 1-4.5-1.5-4.5-4 4-9 8.5-9Zm-6.239-2.391c1.069 1.85.927 4.046-.548 4.898-1.475.851-3.447-.124-4.516-1.975-1.068-1.85-.927-4.046.548-4.898 1.475-.851 3.448.124 4.516 1.975Zm16.994-1.975c1.475.852 1.616 3.048.548 4.898-1.069 1.85-3.041 2.826-4.516 1.975-1.475-.852-1.617-3.048-.548-4.898 1.068-1.851 3.04-2.826 4.516-1.975ZM15.56 8.537c.26 2.121-.728 4.087-2.419 4.295-1.69.207-3.125-1.461-3.385-3.582-.26-2.12.728-4.087 2.418-4.294 1.69-.208 3.125 1.46 3.386 3.581Zm4.264-3.581c1.69.207 2.678 2.173 2.418 4.294-.26 2.121-1.695 3.79-3.385 3.582-1.69-.208-2.679-2.174-2.419-4.295.26-2.12 1.696-3.789 3.386-3.581Z"/>
|
|
15
15
|
</g>
|
|
16
16
|
<defs>
|
|
17
|
-
<filter id="
|
|
17
|
+
<filter id="bsales2" width="103.1%" height="103.1%" x="-1.6%" y="-1.6%" filterUnits="objectBoundingBox">
|
|
18
18
|
<feOffset dy=".5" in="SourceAlpha" result="shadowOffsetInner1"/>
|
|
19
19
|
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" k2="-1" k3="1" operator="arithmetic" result="shadowInnerInner1"/>
|
|
20
20
|
<feColorMatrix in="shadowInnerInner1" result="shadowMatrixInner1" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.25 0"/>
|
|
@@ -26,20 +26,20 @@
|
|
|
26
26
|
<feMergeNode in="shadowMatrixInner2"/>
|
|
27
27
|
</feMerge>
|
|
28
28
|
</filter>
|
|
29
|
-
<filter id="
|
|
29
|
+
<filter id="dsales2" width="105.2%" height="105.2%" x="-2.6%" y="-2.6%" filterUnits="objectBoundingBox">
|
|
30
30
|
<feOffset dy=".5" in="SourceAlpha" result="shadowOffsetInner1"/>
|
|
31
31
|
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" k2="-1" k3="1" operator="arithmetic" result="shadowInnerInner1"/>
|
|
32
32
|
<feColorMatrix in="shadowInnerInner1" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
|
33
33
|
</filter>
|
|
34
|
-
<circle id="
|
|
35
|
-
<path id="
|
|
34
|
+
<circle id="asales2" cx="24" cy="24" r="8"/>
|
|
35
|
+
<path id="csales2" d="M28.42 19.33a.25.25 0 0 1 .25.25v5.146a.25.25 0 0 1-.426.177L26.73 23.39l-5.104 5.105a1.5 1.5 0 1 1-2.122-2.122l5.104-5.104-1.512-1.513a.25.25 0 0 1-.064-.112l-.009-.064a.25.25 0 0 1 .25-.25Z"/>
|
|
36
36
|
</defs>
|
|
37
37
|
<g fill="none" fill-rule="evenodd">
|
|
38
|
-
<use xlink:href="#
|
|
39
|
-
<use xlink:href="#
|
|
38
|
+
<use xlink:href="#asales2" fill="#01B201"/>
|
|
39
|
+
<use xlink:href="#asales2" fill="#000" filter="url(#bsales2)"/>
|
|
40
40
|
<g fill-rule="nonzero">
|
|
41
|
-
<use xlink:href="#
|
|
42
|
-
<use xlink:href="#
|
|
41
|
+
<use xlink:href="#csales2" fill="#FFF"/>
|
|
42
|
+
<use xlink:href="#csales2" fill="#000" filter="url(#dsales2)"/>
|
|
43
43
|
</g>
|
|
44
44
|
</g>
|
|
45
45
|
</svg>
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import TerrierPart from "./parts/terrier-part"
|
|
2
|
+
import {PartTag} from "tuff-core/parts"
|
|
3
|
+
import Messages from "tuff-core/messages"
|
|
4
|
+
import {Logger} from "tuff-core/logging"
|
|
5
|
+
import Html from "tuff-core/html"
|
|
6
|
+
|
|
7
|
+
const log = new Logger('List Viewer')
|
|
8
|
+
|
|
9
|
+
const detailsSelector = '.tt-list-viewer-details'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Part for viewing a list of items and the details associated with them.
|
|
13
|
+
* Each item must have an `id` so that they can be distinguished.
|
|
14
|
+
*/
|
|
15
|
+
export abstract class ListViewerPart<T extends {id: string}> extends TerrierPart<any> {
|
|
16
|
+
|
|
17
|
+
items: T[] = []
|
|
18
|
+
itemMap: Record<string, T> = {}
|
|
19
|
+
|
|
20
|
+
itemClickedKey = Messages.typedKey<{id: string}>()
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A message with this key gets emitted whenever the details are shown.
|
|
24
|
+
*/
|
|
25
|
+
detailsShownKey = Messages.typedKey<{ id: string }>()
|
|
26
|
+
|
|
27
|
+
async init() {
|
|
28
|
+
await super.init()
|
|
29
|
+
|
|
30
|
+
await this.reload()
|
|
31
|
+
|
|
32
|
+
this.onClick(this.itemClickedKey, m => {
|
|
33
|
+
log.info(`Clicked on list item ${m.data.id}`, m)
|
|
34
|
+
this.showDetails(m.data.id)
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
/// Fetching
|
|
40
|
+
|
|
41
|
+
abstract fetchItems(): Promise<T[]>
|
|
42
|
+
|
|
43
|
+
async reload() {
|
|
44
|
+
this.items = await this.fetchItems()
|
|
45
|
+
this.items.forEach((item) => {
|
|
46
|
+
this.itemMap[item.id] = item
|
|
47
|
+
})
|
|
48
|
+
this.dirty()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
/// Rendering
|
|
53
|
+
|
|
54
|
+
get parentClasses(): Array<string> {
|
|
55
|
+
return ['tt-list-viewer']
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
render(parent: PartTag): any {
|
|
59
|
+
parent.div('.tt-list-viewer-list', list => {
|
|
60
|
+
for (const item of this.items) {
|
|
61
|
+
list.a('.tt-list-viewer-item', {id: `item-${item.id}`}, itemView => {
|
|
62
|
+
this.renderListItem(itemView, item)
|
|
63
|
+
}).emitClick(this.itemClickedKey, {id: item.id})
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
parent.div('.tt-list-viewer-details-container', detailsView => {
|
|
67
|
+
detailsView.div(detailsSelector)
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
abstract renderListItem(parent: PartTag, item: T): any
|
|
71
|
+
|
|
72
|
+
abstract renderItemDetail(parent: PartTag, item: T): any
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
// Details
|
|
76
|
+
|
|
77
|
+
private setCurrent(id: string) {
|
|
78
|
+
// clear any existing current item
|
|
79
|
+
const existingCurrents = this.element!.querySelectorAll('.tt-list-viewer-list a.current')
|
|
80
|
+
existingCurrents.forEach((elem) => {
|
|
81
|
+
elem.classList.remove('current')
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// add .current to the new item
|
|
85
|
+
const itemView = this.element!.querySelector(`#item-${id}`)
|
|
86
|
+
if (itemView) {
|
|
87
|
+
itemView.classList.add('current')
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Show the details view for the item with the given id
|
|
93
|
+
* @param id
|
|
94
|
+
*/
|
|
95
|
+
showDetails(id: string) {
|
|
96
|
+
this.setCurrent(id)
|
|
97
|
+
const item = this.itemMap[id]
|
|
98
|
+
if (!item) {
|
|
99
|
+
throw `No item ${id}`
|
|
100
|
+
}
|
|
101
|
+
const container = this.element!.querySelector(detailsSelector)
|
|
102
|
+
if (container) {
|
|
103
|
+
// render the details
|
|
104
|
+
const detailsView = Html.createElement('div', div => {
|
|
105
|
+
this.renderItemDetail(div, item)
|
|
106
|
+
})
|
|
107
|
+
container.innerHTML = detailsView.innerHTML
|
|
108
|
+
this.arrangeDetails(id, container as HTMLElement)
|
|
109
|
+
|
|
110
|
+
// let the world know
|
|
111
|
+
this.emitMessage(this.detailsShownKey, {id})
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
log.warn(`Tried to show item ${id} but there was no ${detailsSelector}`)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* If necessary, move the details next to the item
|
|
120
|
+
* @param id the item id
|
|
121
|
+
* @param detailsView
|
|
122
|
+
*/
|
|
123
|
+
arrangeDetails(id: string, detailsView: HTMLElement) {
|
|
124
|
+
const itemView = this.element!.querySelector(`#item-${id}`)
|
|
125
|
+
if (itemView) {
|
|
126
|
+
// const listView = itemIVew.parentElement
|
|
127
|
+
log.info(`Item is ${itemView.clientWidth} wide and the window is ${window.innerWidth} wide`)
|
|
128
|
+
// crude but effective way to determine if the list is collapsed due to the media breakpoint
|
|
129
|
+
if (itemView.clientWidth > window.innerWidth * 0.8) {
|
|
130
|
+
// move the details to right after the list item
|
|
131
|
+
itemView.after(detailsView)
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// move the details back to the container
|
|
135
|
+
const detailsContainer = this.element!.querySelector(`.tt-list-viewer-details-container`)
|
|
136
|
+
if (detailsContainer) {
|
|
137
|
+
detailsContainer.append(detailsView)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
log.warn(`No item view for ${id}`)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
}
|