terrier-engine 4.3.0 → 4.4.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/api.ts +120 -1
- package/app.ts +56 -0
- package/attachments.ts +55 -0
- package/format.ts +24 -0
- package/forms.ts +52 -1
- package/fragments.ts +13 -1
- package/glyps.ts +2 -2
- package/ids.ts +12 -0
- package/modals.ts +35 -1
- package/overlays.ts +1 -1
- package/package.json +4 -2
- package/parts/terrier-part.ts +45 -2
- package/schema.ts +29 -1
- package/sheets.ts +100 -0
- package/tabs.ts +29 -3
- package/theme.ts +8 -8
package/api.ts
CHANGED
|
@@ -4,6 +4,11 @@ import { QueryParams } from "tuff-core/urls"
|
|
|
4
4
|
const log = new Logger('Api')
|
|
5
5
|
log.level = 'debug'
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
9
|
+
// Basic Requests
|
|
10
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
|
|
7
12
|
/**
|
|
8
13
|
* All API responses containing these fields.
|
|
9
14
|
*/
|
|
@@ -104,9 +109,123 @@ async function post<ResponseType>(url: string, body: Record<string, unknown> | F
|
|
|
104
109
|
}
|
|
105
110
|
|
|
106
111
|
|
|
112
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
113
|
+
// Event Streams
|
|
114
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
115
|
+
|
|
116
|
+
type LogLevel = 'success' | 'info' | 'warn' | 'debug'
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Type of log events from a streaming response.
|
|
120
|
+
*/
|
|
121
|
+
export type LogEvent = {
|
|
122
|
+
level: LogLevel
|
|
123
|
+
prefix?: string
|
|
124
|
+
message: string
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Type of error events from a streaming response.
|
|
129
|
+
*/
|
|
130
|
+
export type ErrorEvent = {
|
|
131
|
+
prefix?: string
|
|
132
|
+
message: string
|
|
133
|
+
backtrace: string[]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Configure a `Streamer`.
|
|
138
|
+
*/
|
|
139
|
+
export type StreamOptions = {
|
|
140
|
+
keepAlive?: boolean
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
type noArgListener = () => any
|
|
144
|
+
|
|
145
|
+
type StreamLifecycle = 'close'
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Exposes a typesafe API for handling streaming responses using SSE.
|
|
149
|
+
*/
|
|
150
|
+
export class Streamer {
|
|
151
|
+
|
|
152
|
+
sse!: EventSource
|
|
153
|
+
lifecycleListeners: Record<StreamLifecycle, noArgListener[]> = {
|
|
154
|
+
close: []
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
constructor(readonly url: string, readonly options: StreamOptions) {
|
|
158
|
+
this.sse = new EventSource(url)
|
|
159
|
+
|
|
160
|
+
// this is a special event sent by the ResponseStreamer on the server
|
|
161
|
+
// to tell us that the request is done
|
|
162
|
+
this.sse.addEventListener('_close', evt => {
|
|
163
|
+
if (!this.options.keepAlive) {
|
|
164
|
+
log.debug(`Closing Streamer at ${url}`, evt)
|
|
165
|
+
this.sse.close()
|
|
166
|
+
for (const listener of this.lifecycleListeners['close']) {
|
|
167
|
+
listener()
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Register a listener for events of the given type.
|
|
175
|
+
* @param type
|
|
176
|
+
* @param listener
|
|
177
|
+
*/
|
|
178
|
+
on<T>(type: string, listener: (event: T) => any) {
|
|
179
|
+
this.sse.addEventListener(type, event => {
|
|
180
|
+
const data = JSON.parse(event.data) as T
|
|
181
|
+
log.debug(`${type} event`, data)
|
|
182
|
+
listener(data)
|
|
183
|
+
})
|
|
184
|
+
return this
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Listen specifically for log events.
|
|
189
|
+
* @param listener
|
|
190
|
+
*/
|
|
191
|
+
onLog(listener: (event: LogEvent) => any) {
|
|
192
|
+
return this.on<LogEvent>('_log', listener)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Listen specifically for error events.
|
|
197
|
+
* @param listener
|
|
198
|
+
*/
|
|
199
|
+
onError(listener: (event: ErrorEvent) => any) {
|
|
200
|
+
return this.on<ErrorEvent>('_error', listener)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
onClose(listener: noArgListener) {
|
|
204
|
+
this.lifecycleListeners['close'].push(listener)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Creates a streaming response for the given endpoint.
|
|
212
|
+
* @param url
|
|
213
|
+
* @param options pass `keepAlive: true` to keep retrying the request
|
|
214
|
+
* @return a `Streamer` on which you attach event handlers
|
|
215
|
+
*/
|
|
216
|
+
function stream(url: string, options: StreamOptions={}): Streamer {
|
|
217
|
+
return new Streamer(url, options)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
222
|
+
// Export
|
|
223
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
224
|
+
|
|
107
225
|
const Api = {
|
|
108
226
|
safeGet,
|
|
109
227
|
safePost,
|
|
110
|
-
post
|
|
228
|
+
post,
|
|
229
|
+
stream
|
|
111
230
|
}
|
|
112
231
|
export default Api
|
package/app.ts
CHANGED
|
@@ -9,6 +9,8 @@ import {OverlayLayerType, OverlayPart} from "./overlays"
|
|
|
9
9
|
|
|
10
10
|
// @ts-ignore
|
|
11
11
|
import logoUrl from './images/optimized/terrier-hub-logo-light.svg'
|
|
12
|
+
import Sheets, {AlertSheetState, ConfirmSheetState, Sheet, SheetState} from "./sheets";
|
|
13
|
+
import {messages} from "tuff-core";
|
|
12
14
|
|
|
13
15
|
const log = new Logger('App')
|
|
14
16
|
Logger.level = 'info'
|
|
@@ -93,4 +95,58 @@ export abstract class TerrierApp<TState> extends TerrierPart<TState> {
|
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
|
|
98
|
+
/// Sheets
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Shows a confirm sheet to the user, asking them a question
|
|
102
|
+
* @param options
|
|
103
|
+
* @param callback gets called if the user hits "Confirm"
|
|
104
|
+
*/
|
|
105
|
+
confirm(options: ConfirmSheetState, callback: () => any) {
|
|
106
|
+
const key = messages.untypedKey()
|
|
107
|
+
const state = {...options,
|
|
108
|
+
primaryActions: [
|
|
109
|
+
{
|
|
110
|
+
title: 'Confirm',
|
|
111
|
+
icon: 'glyp-checkmark',
|
|
112
|
+
click: {key}
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
secondaryActions: [
|
|
116
|
+
{
|
|
117
|
+
title: 'Cancel',
|
|
118
|
+
icon: 'glyp-close',
|
|
119
|
+
classes: ['secondary'],
|
|
120
|
+
click: {key: Sheets.clearKey}
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
} as SheetState
|
|
124
|
+
const sheet = this.overlayPart.getOrCreateLayer(Sheet<SheetState>, state, 'sheet')
|
|
125
|
+
sheet.onClick(key, _ => {
|
|
126
|
+
sheet.clear()
|
|
127
|
+
callback()
|
|
128
|
+
})
|
|
129
|
+
sheet.dirty()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Shows an alert sheet to the user with a message (but no choices).
|
|
134
|
+
* @param options
|
|
135
|
+
*/
|
|
136
|
+
alert(options: AlertSheetState) {
|
|
137
|
+
const state = {...options,
|
|
138
|
+
primaryActions: [
|
|
139
|
+
{
|
|
140
|
+
title: 'Okay',
|
|
141
|
+
icon: 'glyp-checkmark',
|
|
142
|
+
click: {key: Sheets.clearKey},
|
|
143
|
+
classes: ['secondary']
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
} as SheetState
|
|
147
|
+
const sheet = this.overlayPart.getOrCreateLayer(Sheet<SheetState>, state, 'sheet')
|
|
148
|
+
sheet.dirty()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
96
152
|
}
|
package/attachments.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {Logger} from "tuff-core/logging"
|
|
2
|
+
|
|
3
|
+
const log = new Logger('Attachments')
|
|
4
|
+
|
|
5
|
+
type MetaData = {
|
|
6
|
+
size: number
|
|
7
|
+
filename: string
|
|
8
|
+
mime_type: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type ShrineAttachment = {
|
|
12
|
+
id: string
|
|
13
|
+
storage: string
|
|
14
|
+
metadata: MetaData
|
|
15
|
+
path?: string // used to send tmp filepath to server
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type Derivatives = {
|
|
19
|
+
thumbnail?: ShrineAttachment
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type Attachment = ShrineAttachment & { derivatives?: Derivatives }
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Constructs a url pointing to the original or a derivative of the attachment
|
|
26
|
+
* @param attachment
|
|
27
|
+
* @param derivative
|
|
28
|
+
*/
|
|
29
|
+
function url(attachment: Attachment, derivative: keyof Derivatives | null) {
|
|
30
|
+
let path = '/uploads'
|
|
31
|
+
let id
|
|
32
|
+
|
|
33
|
+
if (derivative?.length && attachment.derivatives) {
|
|
34
|
+
const derivativeId = attachment.derivatives[derivative]?.id
|
|
35
|
+
if (derivativeId?.length) {
|
|
36
|
+
path += '/permanent'
|
|
37
|
+
id = derivativeId
|
|
38
|
+
} else {
|
|
39
|
+
log.info(`${derivative} not found, returning original`)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!id) {
|
|
44
|
+
path += '/cache'
|
|
45
|
+
id = attachment.id
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return `${path}/${id}`
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const Attachments = {
|
|
52
|
+
url
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default Attachments
|
package/format.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
3
|
+
// Currency
|
|
4
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Formats a cents value as a dollars string.
|
|
8
|
+
* @param c an integer number of cents
|
|
9
|
+
*/
|
|
10
|
+
function cents(c: number | string): string {
|
|
11
|
+
const num = typeof c == 'number' ? c : parseInt(c)
|
|
12
|
+
return '$' + (num/100).toFixed(2)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
17
|
+
// Export
|
|
18
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
19
|
+
|
|
20
|
+
const Format = {
|
|
21
|
+
cents
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default Format
|
package/forms.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {SelectOptions} from "tuff-core/forms"
|
|
1
|
+
import {Field, FormFields, FormPartData, InputType, KeyOfType, SelectOptions} from "tuff-core/forms"
|
|
2
2
|
import {strings} from "tuff-core"
|
|
3
|
+
import {DbErrors} from "./db-client"
|
|
4
|
+
import {PartTag} from "tuff-core/parts"
|
|
5
|
+
import {InputTag, InputTagAttrs} from "tuff-core/html"
|
|
6
|
+
import TerrierPart from "./parts/terrier-part";
|
|
7
|
+
import {an} from "vitest/dist/types-ad1c3f45";
|
|
3
8
|
|
|
4
9
|
////////////////////////////////////////////////////////////////////////////////
|
|
5
10
|
// Options
|
|
@@ -20,6 +25,52 @@ function titleizeOptions(opts: string[], blank?: string): SelectOptions {
|
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
|
|
28
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
29
|
+
// Form Fields
|
|
30
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Override regular `FormFields` methods to include validation errors.
|
|
34
|
+
*/
|
|
35
|
+
export class TerrierFormFields<T extends FormPartData> extends FormFields<T> {
|
|
36
|
+
|
|
37
|
+
errors?: DbErrors<T>
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* You must pass a `TerrierPart` so that we can render the error bubble with it.
|
|
41
|
+
* @param part
|
|
42
|
+
* @param data
|
|
43
|
+
* @param errors
|
|
44
|
+
*/
|
|
45
|
+
constructor(part: TerrierPart<any>, data: T, errors?: DbErrors<T>) {
|
|
46
|
+
super(part, data)
|
|
47
|
+
this.errors = errors
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
protected input<Key extends KeyOfType<T, any> & string>(parent: PartTag, type: InputType, name: Key, serializerType: {
|
|
51
|
+
new(name: string): Field<any, Element>
|
|
52
|
+
}, attrs?: InputTagAttrs): InputTag {
|
|
53
|
+
if (this.errors && this.errors[name]) {
|
|
54
|
+
attrs ||= {}
|
|
55
|
+
attrs.classes ||= []
|
|
56
|
+
attrs.classes.push('error')
|
|
57
|
+
}
|
|
58
|
+
return super.input(parent, type, name, serializerType, attrs);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Only renders the error bubble if the errors are set.
|
|
63
|
+
* @param parent
|
|
64
|
+
*/
|
|
65
|
+
renderErrorBubble(parent: PartTag) {
|
|
66
|
+
if (this.errors) {
|
|
67
|
+
(this.part as TerrierPart<an>).renderErrorBubble(parent, this.errors)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
23
74
|
////////////////////////////////////////////////////////////////////////////////
|
|
24
75
|
// Export
|
|
25
76
|
////////////////////////////////////////////////////////////////////////////////
|
package/fragments.ts
CHANGED
|
@@ -11,6 +11,18 @@ abstract class ContentFragment {
|
|
|
11
11
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
|
|
15
|
+
protected _classes: string[] = []
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Add arbitrary classes to the top-level fragment.
|
|
19
|
+
* @param c
|
|
20
|
+
*/
|
|
21
|
+
classes(...c: string[]) {
|
|
22
|
+
this._classes.push(...c)
|
|
23
|
+
return this
|
|
24
|
+
}
|
|
25
|
+
|
|
14
26
|
protected _title?: string
|
|
15
27
|
|
|
16
28
|
/**
|
|
@@ -93,7 +105,7 @@ export class PanelFragment extends ContentFragment {
|
|
|
93
105
|
}
|
|
94
106
|
})
|
|
95
107
|
panelActions(panel, this.actions, this.theme)
|
|
96
|
-
})
|
|
108
|
+
}).class(...this._classes)
|
|
97
109
|
}
|
|
98
110
|
|
|
99
111
|
}
|
package/glyps.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// This file was automatically generated by glyps:compile on 06/
|
|
1
|
+
// This file was automatically generated by glyps:compile on 06/18/23 3:03 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-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-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/ids.ts
ADDED
package/modals.ts
CHANGED
|
@@ -44,7 +44,11 @@ export abstract class ModalPart<TState> extends ContentPart<TState> {
|
|
|
44
44
|
const secondaryActions = this.getActions('secondary')
|
|
45
45
|
const primaryActions = this.getActions('primary')
|
|
46
46
|
if (secondaryActions.length || primaryActions.length) {
|
|
47
|
-
|
|
47
|
+
const actionsClasses = ['modal-actions']
|
|
48
|
+
if (this._actionLoading) {
|
|
49
|
+
actionsClasses.push('loading')
|
|
50
|
+
}
|
|
51
|
+
parent.div(...actionsClasses, actions => {
|
|
48
52
|
actions.div('.secondary-actions', container => {
|
|
49
53
|
this.theme.renderActions(container, secondaryActions, {iconColor: 'white', defaultClass: 'secondary'})
|
|
50
54
|
})
|
|
@@ -55,6 +59,36 @@ export abstract class ModalPart<TState> extends ContentPart<TState> {
|
|
|
55
59
|
}
|
|
56
60
|
}
|
|
57
61
|
|
|
62
|
+
private _actionLoading = false
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Blurs the actions to indicate that the modal is doing something.
|
|
66
|
+
*/
|
|
67
|
+
startActionLoading() {
|
|
68
|
+
const elem = this.element
|
|
69
|
+
if (elem) {
|
|
70
|
+
const actions = elem.querySelector('.modal-actions')
|
|
71
|
+
if (actions) {
|
|
72
|
+
this._actionLoading = true
|
|
73
|
+
actions.classList.add('loading')
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Call after calling `startActionLoading()` to remove the loading effect.
|
|
80
|
+
*/
|
|
81
|
+
stopActionLoading() {
|
|
82
|
+
this._actionLoading = false
|
|
83
|
+
const elem = this.element
|
|
84
|
+
if (elem) {
|
|
85
|
+
const actions = elem.querySelector('.modal-actions')
|
|
86
|
+
if (actions) {
|
|
87
|
+
actions.classList.remove('loading')
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
58
92
|
}
|
|
59
93
|
|
|
60
94
|
|
package/overlays.ts
CHANGED
|
@@ -9,7 +9,7 @@ const log = new Logger('Overlays')
|
|
|
9
9
|
// Part
|
|
10
10
|
////////////////////////////////////////////////////////////////////////////////
|
|
11
11
|
|
|
12
|
-
const OverlayLayerTypes = ['modal', 'dropdown', 'lightbox', 'jump'] as const
|
|
12
|
+
const OverlayLayerTypes = ['modal', 'dropdown', 'lightbox', 'jump', 'sheet'] as const
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* The type of overlay for any given layer.
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"files": [
|
|
5
5
|
"*"
|
|
6
6
|
],
|
|
7
|
-
"version": "4.
|
|
7
|
+
"version": "4.4.0",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "https://github.com/Terrier-Tech/terrier-engine"
|
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
"test": "vitest"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
+
"@types/turbolinks": "^5.2.0",
|
|
18
19
|
"dayjs": "^1.11.7",
|
|
19
20
|
"inflection": "^2.0.1",
|
|
20
|
-
"tuff-core": "latest"
|
|
21
|
+
"tuff-core": "latest",
|
|
22
|
+
"turbolinks": "^5.2.0"
|
|
21
23
|
},
|
|
22
24
|
"devDependencies": {
|
|
23
25
|
"@types/node": "^18.16.3",
|
package/parts/terrier-part.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {Part} from "tuff-core/parts"
|
|
1
|
+
import {Part, PartTag} from "tuff-core/parts"
|
|
2
2
|
import {TerrierApp} from "../app"
|
|
3
3
|
import Loading from "../loading"
|
|
4
|
-
import Theme from "../theme"
|
|
4
|
+
import Theme, {IconName} from "../theme"
|
|
5
5
|
import Toasts, {ToastOptions} from "../toasts"
|
|
6
|
+
import {DbErrors} from "../db-client"
|
|
7
|
+
import inflection from "inflection";
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* Base class for ALL parts in a Terrier application.
|
|
@@ -75,6 +77,20 @@ export default abstract class TerrierPart<TState> extends Part<TState> {
|
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
|
|
80
|
+
/// Errors
|
|
81
|
+
|
|
82
|
+
renderErrorBubble(parent: PartTag, errors: DbErrors<any>) {
|
|
83
|
+
parent.div('.tt-bubble.alert', bubble => {
|
|
84
|
+
bubble.ul(ul => {
|
|
85
|
+
for (const kv of Object.entries(errors)) {
|
|
86
|
+
const name = inflection.titleize(kv[0])
|
|
87
|
+
ul.li().text(`${name} ${kv[1]}`)
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
78
94
|
/// Toasts
|
|
79
95
|
|
|
80
96
|
/**
|
|
@@ -86,4 +102,31 @@ export default abstract class TerrierPart<TState> extends Part<TState> {
|
|
|
86
102
|
Toasts.show(message, options, this.theme)
|
|
87
103
|
}
|
|
88
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Show an alert toast with the given message.
|
|
107
|
+
* @param message
|
|
108
|
+
* @param icon
|
|
109
|
+
*/
|
|
110
|
+
alertToast(message: string, icon?: IconName) {
|
|
111
|
+
this.showToast(message, {icon, color: 'alert'})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Show an info toast with the given message.
|
|
116
|
+
* @param message
|
|
117
|
+
* @param icon
|
|
118
|
+
*/
|
|
119
|
+
infoToast(message: string, icon?: IconName) {
|
|
120
|
+
this.showToast(message, {icon, color: 'primary'})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Show a success toast with the given message.
|
|
125
|
+
* @param message
|
|
126
|
+
* @param icon
|
|
127
|
+
*/
|
|
128
|
+
successToast(message: string, icon?: IconName) {
|
|
129
|
+
this.showToast(message, {icon, color: 'success'})
|
|
130
|
+
}
|
|
131
|
+
|
|
89
132
|
}
|
package/schema.ts
CHANGED
|
@@ -38,10 +38,15 @@ export type HasManyDef = {
|
|
|
38
38
|
* Definition for a single model in the schema.
|
|
39
39
|
*/
|
|
40
40
|
export type ModelDef = {
|
|
41
|
+
name: string
|
|
41
42
|
table_name: string
|
|
42
43
|
columns: Record<string, ColumnDef>
|
|
43
44
|
belongs_to: Record<string, BelongsToDef>
|
|
44
45
|
has_many: Record<string, HasManyDef>
|
|
46
|
+
metadata?: {
|
|
47
|
+
description?: string
|
|
48
|
+
common?: boolean
|
|
49
|
+
}
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
/**
|
|
@@ -82,13 +87,36 @@ function belongsToDisplay(belongsTo: BelongsToDef): string {
|
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
|
|
90
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
91
|
+
// Meta
|
|
92
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Gets all models with common=true in the metadata.
|
|
96
|
+
* @param schema
|
|
97
|
+
*/
|
|
98
|
+
function commonModels(schema: SchemaDef): ModelDef[] {
|
|
99
|
+
return Object.values(schema.models).filter(m => m.metadata?.common)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Gets all models with common=false (or not defined) in the metadata.
|
|
104
|
+
* @param schema
|
|
105
|
+
*/
|
|
106
|
+
function uncommonModels(schema: SchemaDef): ModelDef[] {
|
|
107
|
+
return Object.values(schema.models).filter(m => !(m.metadata?.common))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
85
111
|
////////////////////////////////////////////////////////////////////////////////
|
|
86
112
|
// Export
|
|
87
113
|
////////////////////////////////////////////////////////////////////////////////
|
|
88
114
|
|
|
89
115
|
const Schema = {
|
|
90
116
|
get,
|
|
91
|
-
belongsToDisplay
|
|
117
|
+
belongsToDisplay,
|
|
118
|
+
commonModels,
|
|
119
|
+
uncommonModels
|
|
92
120
|
}
|
|
93
121
|
|
|
94
122
|
export default Schema
|
package/sheets.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {Action, IconName} from "./theme"
|
|
2
|
+
import TerrierPart from "./parts/terrier-part"
|
|
3
|
+
import {PartTag} from "tuff-core/parts"
|
|
4
|
+
import Fragments from "./fragments"
|
|
5
|
+
import {messages} from "tuff-core"
|
|
6
|
+
import {Logger} from "tuff-core/logging"
|
|
7
|
+
|
|
8
|
+
const log = new Logger('Sheets')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
12
|
+
// Parts
|
|
13
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
14
|
+
|
|
15
|
+
export type SheetState = {
|
|
16
|
+
title: string
|
|
17
|
+
icon: IconName
|
|
18
|
+
body: string
|
|
19
|
+
primaryActions?: Action[]
|
|
20
|
+
secondaryActions?: Action[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const clearKey = messages.untypedKey()
|
|
24
|
+
|
|
25
|
+
export class Sheet<TState extends SheetState> extends TerrierPart<TState> {
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Removes itself from the DOM.
|
|
29
|
+
*/
|
|
30
|
+
clear() {
|
|
31
|
+
log.info("Clearing sheet")
|
|
32
|
+
this.app.removeOverlay(this.state)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async init() {
|
|
36
|
+
this.onClick(clearKey, _ => {
|
|
37
|
+
this.clear()
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get parentClasses(): Array<string> {
|
|
42
|
+
return ['tt-sheet']
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
render(parent: PartTag): any {
|
|
46
|
+
parent.div('.tt-sheet-backdrop')
|
|
47
|
+
const panel = Fragments.panel(this.theme)
|
|
48
|
+
.title(this.state.title)
|
|
49
|
+
.icon(this.state.icon)
|
|
50
|
+
.content(panel => {
|
|
51
|
+
panel.class('padded')
|
|
52
|
+
panel.div('.body').text(this.state.body)
|
|
53
|
+
})
|
|
54
|
+
for (const action of this.state.primaryActions || []) {
|
|
55
|
+
// if it doesn't have a click key, it must be a close button
|
|
56
|
+
action.click ||= {key: clearKey}
|
|
57
|
+
panel.addAction(action, 'primary')
|
|
58
|
+
}
|
|
59
|
+
for (const action of this.state.secondaryActions || []) {
|
|
60
|
+
// if it doesn't have a click key, it must be a close button
|
|
61
|
+
action.click ||= {key: clearKey}
|
|
62
|
+
panel.addAction(action, 'secondary')
|
|
63
|
+
}
|
|
64
|
+
panel.render(parent)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
update(_elem: HTMLElement) {
|
|
68
|
+
setTimeout(
|
|
69
|
+
() => _elem.classList.add('show'),
|
|
70
|
+
10
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
78
|
+
// Types
|
|
79
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* State type for a sheet that asks the user to confirm a choice.
|
|
83
|
+
*/
|
|
84
|
+
export type ConfirmSheetState = Pick<SheetState, 'title' | 'body' | 'icon'>
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* State type for a sheet that tells the user something with no options.
|
|
88
|
+
*/
|
|
89
|
+
export type AlertSheetState = Pick<SheetState, 'title' | 'body' | 'icon'>
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
93
|
+
// Export
|
|
94
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
95
|
+
|
|
96
|
+
const Sheets = {
|
|
97
|
+
clearKey
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export default Sheets
|
package/tabs.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {Logger} from "tuff-core/logging"
|
|
|
2
2
|
import {typedKey} from "tuff-core/messages"
|
|
3
3
|
import {Part, PartParent, PartTag, StatelessPart} from "tuff-core/parts"
|
|
4
4
|
import TerrierPart from "./parts/terrier-part"
|
|
5
|
-
import {Action, IconName} from "./theme"
|
|
5
|
+
import {Action, IconName, Packet} from "./theme"
|
|
6
6
|
|
|
7
7
|
const log = new Logger("Tabs")
|
|
8
8
|
|
|
@@ -14,6 +14,8 @@ export type TabParams = {
|
|
|
14
14
|
title: string
|
|
15
15
|
icon?: IconName
|
|
16
16
|
state?: 'enabled' | 'disabled' | 'hidden'
|
|
17
|
+
classes?: string[]
|
|
18
|
+
click?: Packet
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
/**
|
|
@@ -81,6 +83,24 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
|
|
|
81
83
|
this.dirty()
|
|
82
84
|
}
|
|
83
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Removes the tab with the given key.
|
|
88
|
+
* @param key
|
|
89
|
+
*/
|
|
90
|
+
removeTab(key: string) {
|
|
91
|
+
const tab = this.tabs[key]
|
|
92
|
+
if (tab) {
|
|
93
|
+
log.info(`Removing tab ${key}`, tab)
|
|
94
|
+
delete this.tabs[key]
|
|
95
|
+
this.removeChild(tab.part)
|
|
96
|
+
this.state.currentTab = undefined
|
|
97
|
+
this.dirty()
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
log.warn(`No tab ${key} to remove!`)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
84
104
|
/**
|
|
85
105
|
* Changes this tab container to show the tab with the given key
|
|
86
106
|
* @param tabKey
|
|
@@ -137,6 +157,9 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
|
|
|
137
157
|
}
|
|
138
158
|
a.span({text: tab.title})
|
|
139
159
|
a.emitClick(this.changeTabKey, {tabKey: tab.key})
|
|
160
|
+
if (tab.click) {
|
|
161
|
+
a.emitClick(tab.click.key, tab.click.data || {})
|
|
162
|
+
}
|
|
140
163
|
})
|
|
141
164
|
}
|
|
142
165
|
if (this._afterAction) {
|
|
@@ -146,9 +169,12 @@ export class TabContainerPart extends TerrierPart<TabContainerState> {
|
|
|
146
169
|
})
|
|
147
170
|
|
|
148
171
|
if (currentTabKey) {
|
|
149
|
-
const
|
|
172
|
+
const currentTab = this.tabs[currentTabKey]
|
|
150
173
|
container.div('.tt-tab-content', panel => {
|
|
151
|
-
|
|
174
|
+
if (currentTab.classes?.length) {
|
|
175
|
+
panel.class(...currentTab.classes)
|
|
176
|
+
}
|
|
177
|
+
panel.part(currentTab.part as StatelessPart)
|
|
152
178
|
})
|
|
153
179
|
}
|
|
154
180
|
})
|
package/theme.ts
CHANGED
|
@@ -11,8 +11,8 @@ export interface ThemeType {
|
|
|
11
11
|
|
|
12
12
|
export type IconName = GlypName | HubIconName
|
|
13
13
|
|
|
14
|
-
const ColorNames = [
|
|
15
|
-
'link', 'primary', 'secondary', 'active', 'pending', 'success', 'alert', 'white', 'inactive'
|
|
14
|
+
export const ColorNames = [
|
|
15
|
+
'link', 'primary', 'secondary', 'active', 'pending', 'success', 'warn', 'alert', 'white', 'inactive', 'super', 'billing', 'docs'
|
|
16
16
|
] as const
|
|
17
17
|
|
|
18
18
|
export type ColorName = typeof ColorNames[number]
|
|
@@ -55,16 +55,13 @@ export default class Theme {
|
|
|
55
55
|
HubIcons.renderIcon(parent, icon as HubIconName, color)
|
|
56
56
|
}
|
|
57
57
|
else { // a regular font icon
|
|
58
|
-
const
|
|
59
|
-
if (color?.length)
|
|
60
|
-
classes.push(color)
|
|
61
|
-
}
|
|
62
|
-
parent.i().class(...classes)
|
|
58
|
+
const iconElem = parent.i('.icon', icon)
|
|
59
|
+
if (color?.length) iconElem.class(color)
|
|
63
60
|
}
|
|
64
61
|
}
|
|
65
62
|
|
|
66
63
|
renderCloseIcon(parent: PartTag, color?: ColorName | null): void {
|
|
67
|
-
const classes = ['glyp-close', 'close']
|
|
64
|
+
const classes = ['icon', 'glyp-close', 'close']
|
|
68
65
|
if (color?.length) {
|
|
69
66
|
classes.push(color)
|
|
70
67
|
}
|
|
@@ -101,6 +98,9 @@ export default class Theme {
|
|
|
101
98
|
if (action.title?.length) {
|
|
102
99
|
a.div('.title', {text: action.title})
|
|
103
100
|
}
|
|
101
|
+
else {
|
|
102
|
+
a.class('icon-only')
|
|
103
|
+
}
|
|
104
104
|
if (action.href?.length) {
|
|
105
105
|
a.attrs({href: action.href})
|
|
106
106
|
}
|