terrier-engine 4.7.3 → 4.7.5
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 +19 -1
- package/data-dive/queries/filters.ts +31 -7
- package/data-dive/queries/queries.ts +34 -4
- package/data-dive/queries/query-editor.ts +12 -5
- package/data-dive/queries/tables.ts +14 -5
- package/data-dive/queries/validation.ts +73 -0
- package/package.json +1 -1
- package/terrier/gen/hub-icons.ts +33 -1
- package/terrier/images/icons/existing_child.svg +1 -0
- package/terrier/images/icons/existing_parent.svg +1 -0
- package/terrier/images/icons/new_child.svg +1 -0
- package/terrier/images/icons/new_parent.svg +1 -0
- package/terrier/images/optimized/icon-existing_child.svg +1 -0
- package/terrier/images/optimized/icon-existing_parent.svg +1 -0
- package/terrier/images/optimized/icon-new_child.svg +1 -0
- package/terrier/images/optimized/icon-new_parent.svg +1 -0
- package/terrier/images/raw/icon-existing_child.svg +11 -0
- package/terrier/images/raw/icon-existing_parent.svg +11 -0
- package/terrier/images/raw/icon-new_child.svg +12 -0
- package/terrier/images/raw/icon-new_parent.svg +12 -0
|
@@ -11,6 +11,7 @@ import {Dropdown} from "../../terrier/dropdowns"
|
|
|
11
11
|
import DiveEditor from "../dives/dive-editor"
|
|
12
12
|
import Messages from "tuff-core/messages"
|
|
13
13
|
import Arrays from "tuff-core/arrays"
|
|
14
|
+
import {ColumnValidationError} from "./validation"
|
|
14
15
|
|
|
15
16
|
const log = new Logger("Columns")
|
|
16
17
|
|
|
@@ -57,9 +58,25 @@ export type ColumnRef = {
|
|
|
57
58
|
alias?: string
|
|
58
59
|
grouped?: boolean
|
|
59
60
|
function?: AggFunction | DateFunction
|
|
61
|
+
errors?: ColumnValidationError[]
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Computes the name of the resulting select clause.
|
|
67
|
+
* @param table
|
|
68
|
+
* @param col
|
|
69
|
+
*/
|
|
70
|
+
function computeSelectName(table: TableRef, col: ColumnRef): string {
|
|
71
|
+
if (col.alias?.length) {
|
|
72
|
+
return col.alias
|
|
73
|
+
} else if (table.prefix?.length) {
|
|
74
|
+
return `${table.prefix}${col.name}`
|
|
75
|
+
} else {
|
|
76
|
+
return col.name
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
63
80
|
|
|
64
81
|
////////////////////////////////////////////////////////////////////////////////
|
|
65
82
|
// Rendering
|
|
@@ -405,7 +422,8 @@ class SelectColumnsDropdown extends Dropdown<{modelDef: ModelDef, callback: Sele
|
|
|
405
422
|
|
|
406
423
|
const Columns = {
|
|
407
424
|
render,
|
|
408
|
-
functionType
|
|
425
|
+
functionType,
|
|
426
|
+
computeSelectName
|
|
409
427
|
}
|
|
410
428
|
|
|
411
429
|
export default Columns
|
|
@@ -13,6 +13,7 @@ import Format from "../../terrier/format"
|
|
|
13
13
|
import DiveEditor from "../dives/dive-editor"
|
|
14
14
|
import Messages from "tuff-core/messages"
|
|
15
15
|
import Arrays from "tuff-core/arrays"
|
|
16
|
+
import {SelectOptions} from "tuff-core/forms"
|
|
16
17
|
|
|
17
18
|
const log = new Logger("Filters")
|
|
18
19
|
|
|
@@ -27,8 +28,33 @@ type BaseFilter = {
|
|
|
27
28
|
edit_label?: string
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
export type DirectOperator = typeof
|
|
31
|
+
const directOperators = ['eq', 'ne', 'ilike', 'lt', 'gt', 'lte', 'gte'] as const
|
|
32
|
+
export type DirectOperator = typeof directOperators[number]
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Computes the operator options for the given column type.
|
|
36
|
+
* @param type
|
|
37
|
+
*/
|
|
38
|
+
function operatorOptions(type: string): SelectOptions {
|
|
39
|
+
let operators: DirectOperator[] = ['eq', 'ne'] // equality is the only thing we can assume for any type
|
|
40
|
+
switch (type) {
|
|
41
|
+
case 'text':
|
|
42
|
+
case 'string':
|
|
43
|
+
operators = ['eq', 'ne', 'ilike']
|
|
44
|
+
break
|
|
45
|
+
case 'float':
|
|
46
|
+
case 'integer':
|
|
47
|
+
case 'cents':
|
|
48
|
+
operators = ['eq', 'ne', 'lt', 'gt', 'lte', 'gte']
|
|
49
|
+
break
|
|
50
|
+
}
|
|
51
|
+
return operators.map(op => {
|
|
52
|
+
return {
|
|
53
|
+
value: op,
|
|
54
|
+
title: operatorDisplay(op)
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
}
|
|
32
58
|
|
|
33
59
|
export type DirectFilter = BaseFilter & {
|
|
34
60
|
filter_type: 'direct'
|
|
@@ -73,7 +99,7 @@ function operatorDisplay(op: DirectOperator): string {
|
|
|
73
99
|
case 'ne':
|
|
74
100
|
return '≠'
|
|
75
101
|
case 'ilike':
|
|
76
|
-
return '
|
|
102
|
+
return '≈'
|
|
77
103
|
case 'lt':
|
|
78
104
|
return '<'
|
|
79
105
|
case 'lte':
|
|
@@ -369,10 +395,8 @@ class DirectFilterEditor extends FilterEditor<DirectFilter> {
|
|
|
369
395
|
col.div('.tt-readonly-field', {text: this.state.column})
|
|
370
396
|
})
|
|
371
397
|
parent.div('.operator', col => {
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
})
|
|
375
|
-
this.select(col, 'operator', operatorOptions)
|
|
398
|
+
const opts = operatorOptions(this.columnDef?.type || 'text')
|
|
399
|
+
this.select(col, 'operator', opts)
|
|
376
400
|
})
|
|
377
401
|
parent.div('.filter', col => {
|
|
378
402
|
switch (this.state.column_type) {
|
|
@@ -11,6 +11,7 @@ import inflection from "inflection"
|
|
|
11
11
|
import Messages from "tuff-core/messages"
|
|
12
12
|
import Strings from "tuff-core/strings"
|
|
13
13
|
import Arrays from "tuff-core/arrays"
|
|
14
|
+
import {ColumnRef} from "./columns"
|
|
14
15
|
|
|
15
16
|
const log = new Logger("Queries")
|
|
16
17
|
|
|
@@ -28,13 +29,41 @@ export type Query = {
|
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
////////////////////////////////////////////////////////////////////////////////
|
|
31
|
-
//
|
|
32
|
+
// Utilities
|
|
33
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
34
|
+
|
|
35
|
+
type ColumnFunction = (table: TableRef, col: ColumnRef) => any
|
|
36
|
+
|
|
37
|
+
function eachColumnForTable(table: TableRef, fn: ColumnFunction) {
|
|
38
|
+
if (table.columns) {
|
|
39
|
+
for (const col of table.columns) {
|
|
40
|
+
fn(table, col)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (table.joins) {
|
|
44
|
+
for (const joinedTable of Object.values(table.joins)) {
|
|
45
|
+
eachColumnForTable(joinedTable, fn)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Recursively iterates over all columns in a query.
|
|
52
|
+
* @param query
|
|
53
|
+
*/
|
|
54
|
+
function eachColumn(query: Query, fn: ColumnFunction) {
|
|
55
|
+
eachColumnForTable(query.from, fn)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
60
|
+
// Server-Side Validation
|
|
32
61
|
////////////////////////////////////////////////////////////////////////////////
|
|
33
62
|
|
|
34
63
|
/**
|
|
35
64
|
* Type for the response to a server-side query validation.
|
|
36
65
|
*/
|
|
37
|
-
export type
|
|
66
|
+
export type QueryServerValidation = ApiResponse & {
|
|
38
67
|
query: Query
|
|
39
68
|
sql?: string
|
|
40
69
|
sql_html?: string
|
|
@@ -47,8 +76,8 @@ export type QueryValidation = ApiResponse & {
|
|
|
47
76
|
* Has the server validate the given query and generate SQL for it.
|
|
48
77
|
* @param query
|
|
49
78
|
*/
|
|
50
|
-
async function validate(query: Query): Promise<
|
|
51
|
-
return await api.post<
|
|
79
|
+
async function validate(query: Query): Promise<QueryServerValidation> {
|
|
80
|
+
return await api.post<QueryServerValidation>("/data_dive/validate_query.json", {query})
|
|
52
81
|
}
|
|
53
82
|
|
|
54
83
|
|
|
@@ -258,6 +287,7 @@ export class QueryModelPicker extends TerrierPart<QueryModelPickerState> {
|
|
|
258
287
|
////////////////////////////////////////////////////////////////////////////////
|
|
259
288
|
|
|
260
289
|
const Queries = {
|
|
290
|
+
eachColumn,
|
|
261
291
|
validate,
|
|
262
292
|
preview,
|
|
263
293
|
renderPreview
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {PartTag} from "tuff-core/parts"
|
|
2
|
-
import Queries, {Query, QueryResult,
|
|
2
|
+
import Queries, {Query, QueryResult, QueryServerValidation} from "./queries"
|
|
3
3
|
import Tables, {FromTableView} from "./tables"
|
|
4
4
|
import {Logger} from "tuff-core/logging"
|
|
5
5
|
import QueryForm, {QuerySettings, QuerySettingsColumns} from "./query-form"
|
|
@@ -9,6 +9,7 @@ import Html from "tuff-core/html"
|
|
|
9
9
|
import ContentPart from "../../terrier/parts/content-part"
|
|
10
10
|
import {TabContainerPart} from "../../terrier/tabs"
|
|
11
11
|
import Messages from "tuff-core/messages"
|
|
12
|
+
import Validation, {QueryClientValidation} from "./validation"
|
|
12
13
|
|
|
13
14
|
const log = new Logger("QueryEditor")
|
|
14
15
|
|
|
@@ -17,7 +18,7 @@ const log = new Logger("QueryEditor")
|
|
|
17
18
|
// Keys
|
|
18
19
|
////////////////////////////////////////////////////////////////////////////////
|
|
19
20
|
|
|
20
|
-
const validationKey = Messages.typedKey<
|
|
21
|
+
const validationKey = Messages.typedKey<QueryServerValidation>()
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
////////////////////////////////////////////////////////////////////////////////
|
|
@@ -60,9 +61,9 @@ class SettingsPart extends ContentPart<SubEditorState> {
|
|
|
60
61
|
|
|
61
62
|
class SqlPart extends ContentPart<SubEditorState> {
|
|
62
63
|
|
|
63
|
-
validation?:
|
|
64
|
+
validation?: QueryServerValidation
|
|
64
65
|
|
|
65
|
-
setValidation(validation:
|
|
66
|
+
setValidation(validation: QueryServerValidation) {
|
|
66
67
|
this.validation = validation
|
|
67
68
|
this.dirty()
|
|
68
69
|
}
|
|
@@ -156,11 +157,13 @@ export default class QueryEditor extends ContentPart<QueryEditorState> {
|
|
|
156
157
|
settingsPart!: SettingsPart
|
|
157
158
|
sqlPart!: SqlPart
|
|
158
159
|
previewPart!: PreviewPart
|
|
160
|
+
clientValidation!: QueryClientValidation
|
|
159
161
|
|
|
160
162
|
updatePreviewKey = Messages.untypedKey()
|
|
161
163
|
|
|
162
164
|
async init() {
|
|
163
165
|
const query = this.state.query
|
|
166
|
+
this.clientValidation = Validation.validateQuery(query)
|
|
164
167
|
|
|
165
168
|
log.info("Initializing query editor", query)
|
|
166
169
|
|
|
@@ -180,7 +183,7 @@ export default class QueryEditor extends ContentPart<QueryEditorState> {
|
|
|
180
183
|
this.previewPart = this.tabs.upsertTab({key: 'preview', title: 'Preview', icon: 'glyp-table', classes: ['no-padding'], click: {key: this.updatePreviewKey}},
|
|
181
184
|
PreviewPart, {editor: this, query})
|
|
182
185
|
|
|
183
|
-
this.tableEditor = this.makePart(FromTableView, {schema: this.state.schema, table: this.state.query.from})
|
|
186
|
+
this.tableEditor = this.makePart(FromTableView, {schema: this.state.schema, queryEditor: this, table: this.state.query.from})
|
|
184
187
|
|
|
185
188
|
this.listenMessage(Tables.updatedKey, m => {
|
|
186
189
|
log.info(`Table ${m.data.model} updated`, m.data)
|
|
@@ -219,10 +222,14 @@ export default class QueryEditor extends ContentPart<QueryEditorState> {
|
|
|
219
222
|
}
|
|
220
223
|
|
|
221
224
|
async validate() {
|
|
225
|
+
// server-side validation
|
|
222
226
|
const res = await Queries.validate(this.state.query)
|
|
223
227
|
log.info(`Query validated`, res)
|
|
224
228
|
this.emitMessage(validationKey, res)
|
|
225
229
|
this.sqlPart.setValidation(res)
|
|
230
|
+
|
|
231
|
+
// client-side validation
|
|
232
|
+
this.clientValidation = Validation.validateQuery(this.state.query)
|
|
226
233
|
this.dirty()
|
|
227
234
|
}
|
|
228
235
|
|
|
@@ -11,6 +11,7 @@ import TerrierFormPart from "../../terrier/parts/terrier-form-part"
|
|
|
11
11
|
import DiveEditor from "../dives/dive-editor"
|
|
12
12
|
import Messages from "tuff-core/messages"
|
|
13
13
|
import Arrays from "tuff-core/arrays"
|
|
14
|
+
import QueryEditor from "./query-editor"
|
|
14
15
|
|
|
15
16
|
const log = new Logger("Tables")
|
|
16
17
|
|
|
@@ -48,7 +49,9 @@ const updatedKey = Messages.typedKey<TableRef>()
|
|
|
48
49
|
* Only keep one (the last one traversed) per table/column combination.
|
|
49
50
|
* This means that some filters may clobber others, but I think it will yield
|
|
50
51
|
* the desired result most of the time.
|
|
52
|
+
* @param schema
|
|
51
53
|
* @param table
|
|
54
|
+
* @param filters
|
|
52
55
|
*/
|
|
53
56
|
function computeFilterInputs(schema: SchemaDef, table: TableRef, filters: Record<string, FilterInput>) {
|
|
54
57
|
for (const f of table.filters || []) {
|
|
@@ -67,7 +70,7 @@ function computeFilterInputs(schema: SchemaDef, table: TableRef, filters: Record
|
|
|
67
70
|
// View
|
|
68
71
|
////////////////////////////////////////////////////////////////////////////////
|
|
69
72
|
|
|
70
|
-
export class TableView<T extends TableRef> extends ContentPart<{ schema: SchemaDef, table: T }> {
|
|
73
|
+
export class TableView<T extends TableRef> extends ContentPart<{ schema: SchemaDef, queryEditor: QueryEditor, table: T }> {
|
|
71
74
|
|
|
72
75
|
schema!: SchemaDef
|
|
73
76
|
table!: T
|
|
@@ -88,7 +91,7 @@ export class TableView<T extends TableRef> extends ContentPart<{ schema: SchemaD
|
|
|
88
91
|
this.modelDef = this.schema.models[this.table.model]
|
|
89
92
|
this.tableName = inflection.titleize(inflection.tableize(this.table.model))
|
|
90
93
|
this.displayName = this.tableName
|
|
91
|
-
this.
|
|
94
|
+
this.updateJoinedViews()
|
|
92
95
|
|
|
93
96
|
this.onClick(this.editColumnsKey, _ => {
|
|
94
97
|
log.info(`Edit ${this.displayName} Columns`)
|
|
@@ -155,7 +158,7 @@ export class TableView<T extends TableRef> extends ContentPart<{ schema: SchemaD
|
|
|
155
158
|
log.info(`Creating joined table`, newTable)
|
|
156
159
|
this.table.joins ||= {}
|
|
157
160
|
this.table.joins[newTable.belongs_to] = newTable
|
|
158
|
-
this.
|
|
161
|
+
this.updateJoinedViews()
|
|
159
162
|
}
|
|
160
163
|
this.app.showModal(JoinedTableEditorModal, {table, belongsTo, callback, parentTable: this.state.table as TableRef})
|
|
161
164
|
}
|
|
@@ -165,9 +168,9 @@ export class TableView<T extends TableRef> extends ContentPart<{ schema: SchemaD
|
|
|
165
168
|
/**
|
|
166
169
|
* Re-generates all views for the joined tables.
|
|
167
170
|
*/
|
|
168
|
-
|
|
171
|
+
updateJoinedViews() {
|
|
169
172
|
const states = Object.values(this.table.joins || {}).map(table => {
|
|
170
|
-
return {schema: this.schema, table}
|
|
173
|
+
return {schema: this.schema, queryEditor: this.state.queryEditor, table}
|
|
171
174
|
})
|
|
172
175
|
this.assignCollection('joined', JoinedTableView, states)
|
|
173
176
|
for (const part of this.getCollectionParts('joined')) {
|
|
@@ -242,6 +245,12 @@ export class TableView<T extends TableRef> extends ContentPart<{ schema: SchemaD
|
|
|
242
245
|
for (const col of this.table.columns) {
|
|
243
246
|
section.div('.column.line', line => {
|
|
244
247
|
Columns.render(line, col)
|
|
248
|
+
if (col.errors?.length) {
|
|
249
|
+
line.class('error')
|
|
250
|
+
for (const error of col.errors) {
|
|
251
|
+
line.div('.error-message').text(error.message)
|
|
252
|
+
}
|
|
253
|
+
}
|
|
245
254
|
})
|
|
246
255
|
}
|
|
247
256
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import Queries, {Query} from "./queries"
|
|
2
|
+
import Columns, {ColumnRef} from "./columns"
|
|
3
|
+
|
|
4
|
+
export type ColumnValidationError = {
|
|
5
|
+
message: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type QueryClientValidation = {
|
|
9
|
+
columns: ColumnValidationError[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
13
|
+
// Client-Side Validation
|
|
14
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Validates that a query will produce valid SQL.
|
|
18
|
+
*/
|
|
19
|
+
function validateQuery(query: Query): QueryClientValidation {
|
|
20
|
+
|
|
21
|
+
const validation: QueryClientValidation = {
|
|
22
|
+
columns: []
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function addColumnError(col: ColumnRef, message: string) {
|
|
26
|
+
const error = {message}
|
|
27
|
+
col.errors ||= []
|
|
28
|
+
col.errors.push(error)
|
|
29
|
+
validation.columns.push(error)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const usedNames: Set<string> = new Set<string>()
|
|
33
|
+
|
|
34
|
+
let isGrouped = false
|
|
35
|
+
Queries.eachColumn(query, (table, col) => {
|
|
36
|
+
// clear the errors
|
|
37
|
+
col.errors = undefined
|
|
38
|
+
|
|
39
|
+
// determine if there's a _group by_ in the query
|
|
40
|
+
if (col.grouped) {
|
|
41
|
+
isGrouped = true
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// each select name should only be used once
|
|
45
|
+
const selectName = Columns.computeSelectName(table, col)
|
|
46
|
+
if (usedNames.has(selectName)) {
|
|
47
|
+
addColumnError(col, `<strong>${selectName}</strong> has already been selected for a different column`)
|
|
48
|
+
}
|
|
49
|
+
usedNames.add(selectName)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// if the query is grouped, ensure that all other column refs
|
|
53
|
+
// are either grouped or have an aggregate function
|
|
54
|
+
if (isGrouped) {
|
|
55
|
+
Queries.eachColumn(query, (_, col) => {
|
|
56
|
+
if (!col.grouped && !col.function) {
|
|
57
|
+
addColumnError(col, `<strong>${col.name}</strong> must be grouped or have an aggregate function`)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return validation
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
67
|
+
// Export
|
|
68
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
69
|
+
|
|
70
|
+
const Validation = {
|
|
71
|
+
validateQuery
|
|
72
|
+
}
|
|
73
|
+
export default Validation
|
package/package.json
CHANGED
package/terrier/gen/hub-icons.ts
CHANGED
|
@@ -117,6 +117,14 @@ import EditRaw from '../images/icons/edit.svg?raw'
|
|
|
117
117
|
// @ts-ignore
|
|
118
118
|
import EditSrc from '../images/icons/edit.svg'
|
|
119
119
|
// @ts-ignore
|
|
120
|
+
import ExistingChildRaw from '../images/icons/existing_child.svg?raw'
|
|
121
|
+
// @ts-ignore
|
|
122
|
+
import ExistingChildSrc from '../images/icons/existing_child.svg'
|
|
123
|
+
// @ts-ignore
|
|
124
|
+
import ExistingParentRaw from '../images/icons/existing_parent.svg?raw'
|
|
125
|
+
// @ts-ignore
|
|
126
|
+
import ExistingParentSrc from '../images/icons/existing_parent.svg'
|
|
127
|
+
// @ts-ignore
|
|
120
128
|
import FeatureRaw from '../images/icons/feature.svg?raw'
|
|
121
129
|
// @ts-ignore
|
|
122
130
|
import FeatureSrc from '../images/icons/feature.svg'
|
|
@@ -209,6 +217,14 @@ import MinusRaw from '../images/icons/minus.svg?raw'
|
|
|
209
217
|
// @ts-ignore
|
|
210
218
|
import MinusSrc from '../images/icons/minus.svg'
|
|
211
219
|
// @ts-ignore
|
|
220
|
+
import NewChildRaw from '../images/icons/new_child.svg?raw'
|
|
221
|
+
// @ts-ignore
|
|
222
|
+
import NewChildSrc from '../images/icons/new_child.svg'
|
|
223
|
+
// @ts-ignore
|
|
224
|
+
import NewParentRaw from '../images/icons/new_parent.svg?raw'
|
|
225
|
+
// @ts-ignore
|
|
226
|
+
import NewParentSrc from '../images/icons/new_parent.svg'
|
|
227
|
+
// @ts-ignore
|
|
212
228
|
import NightRaw from '../images/icons/night.svg?raw'
|
|
213
229
|
// @ts-ignore
|
|
214
230
|
import NightSrc from '../images/icons/night.svg'
|
|
@@ -470,6 +486,14 @@ export const IconDefs: Record<HubIconName,{ raw: string, src: string }> = {
|
|
|
470
486
|
raw: EditRaw,
|
|
471
487
|
src: EditSrc,
|
|
472
488
|
},
|
|
489
|
+
"hub-existing_child": {
|
|
490
|
+
raw: ExistingChildRaw,
|
|
491
|
+
src: ExistingChildSrc,
|
|
492
|
+
},
|
|
493
|
+
"hub-existing_parent": {
|
|
494
|
+
raw: ExistingParentRaw,
|
|
495
|
+
src: ExistingParentSrc,
|
|
496
|
+
},
|
|
473
497
|
"hub-feature": {
|
|
474
498
|
raw: FeatureRaw,
|
|
475
499
|
src: FeatureSrc,
|
|
@@ -562,6 +586,14 @@ export const IconDefs: Record<HubIconName,{ raw: string, src: string }> = {
|
|
|
562
586
|
raw: MinusRaw,
|
|
563
587
|
src: MinusSrc,
|
|
564
588
|
},
|
|
589
|
+
"hub-new_child": {
|
|
590
|
+
raw: NewChildRaw,
|
|
591
|
+
src: NewChildSrc,
|
|
592
|
+
},
|
|
593
|
+
"hub-new_parent": {
|
|
594
|
+
raw: NewParentRaw,
|
|
595
|
+
src: NewParentSrc,
|
|
596
|
+
},
|
|
565
597
|
"hub-night": {
|
|
566
598
|
raw: NightRaw,
|
|
567
599
|
src: NightSrc,
|
|
@@ -713,7 +745,7 @@ export const IconDefs: Record<HubIconName,{ raw: string, src: string }> = {
|
|
|
713
745
|
}
|
|
714
746
|
|
|
715
747
|
const Names = [
|
|
716
|
-
'hub-active', 'hub-admin', 'hub-archive', 'hub-arrow_down', 'hub-arrow_left', 'hub-arrow_right', 'hub-arrow_up', 'hub-assign', 'hub-attachment', 'hub-back', 'hub-badge', 'hub-board', 'hub-branch', 'hub-bug', 'hub-calculator', 'hub-checkmark', 'hub-close', 'hub-clypboard', 'hub-comment', 'hub-complete', 'hub-dashboard', 'hub-data_pull', 'hub-data_update', 'hub-database', 'hub-day', 'hub-delete', 'hub-documentation', 'hub-edit', 'hub-feature', 'hub-flex', 'hub-forward', 'hub-github', 'hub-history', 'hub-home', 'hub-image', 'hub-inbox', 'hub-info', 'hub-internal', 'hub-issue', 'hub-lane', 'hub-lane_asap', 'hub-lane_days', 'hub-lane_hours', 'hub-lane_weeks', 'hub-lanes_board', 'hub-level_complete', 'hub-level_highway', 'hub-level_on_ramp', 'hub-level_parking', 'hub-metrics', 'hub-minus', 'hub-night', 'hub-origin', 'hub-pending', 'hub-plus', 'hub-post', 'hub-posts', 'hub-pr_closed', 'hub-pr_merged', 'hub-pr_open', 'hub-prioritized', 'hub-project', 'hub-question', 'hub-reaction', 'hub-read_mail', 'hub-recent', 'hub-refresh', 'hub-related_posts', 'hub-request', 'hub-settings', 'hub-status', 'hub-step_deploy', 'hub-step_develop', 'hub-step_investigate', 'hub-step_review', 'hub-step_test', 'hub-steps', 'hub-steps_board', 'hub-subscribe', 'hub-support', 'hub-terrier', 'hub-thumbs_up', 'hub-type', 'hub-unprioritized', 'hub-upload', 'hub-user', 'hub-users', 'hub-week'
|
|
748
|
+
'hub-active', 'hub-admin', 'hub-archive', 'hub-arrow_down', 'hub-arrow_left', 'hub-arrow_right', 'hub-arrow_up', 'hub-assign', 'hub-attachment', 'hub-back', 'hub-badge', 'hub-board', 'hub-branch', 'hub-bug', 'hub-calculator', 'hub-checkmark', 'hub-close', 'hub-clypboard', 'hub-comment', 'hub-complete', 'hub-dashboard', 'hub-data_pull', 'hub-data_update', 'hub-database', 'hub-day', 'hub-delete', 'hub-documentation', 'hub-edit', 'hub-existing_child', 'hub-existing_parent', 'hub-feature', 'hub-flex', 'hub-forward', 'hub-github', 'hub-history', 'hub-home', 'hub-image', 'hub-inbox', 'hub-info', 'hub-internal', 'hub-issue', 'hub-lane', 'hub-lane_asap', 'hub-lane_days', 'hub-lane_hours', 'hub-lane_weeks', 'hub-lanes_board', 'hub-level_complete', 'hub-level_highway', 'hub-level_on_ramp', 'hub-level_parking', 'hub-metrics', 'hub-minus', 'hub-new_child', 'hub-new_parent', 'hub-night', 'hub-origin', 'hub-pending', 'hub-plus', 'hub-post', 'hub-posts', 'hub-pr_closed', 'hub-pr_merged', 'hub-pr_open', 'hub-prioritized', 'hub-project', 'hub-question', 'hub-reaction', 'hub-read_mail', 'hub-recent', 'hub-refresh', 'hub-related_posts', 'hub-request', 'hub-settings', 'hub-status', 'hub-step_deploy', 'hub-step_develop', 'hub-step_investigate', 'hub-step_review', 'hub-step_test', 'hub-steps', 'hub-steps_board', 'hub-subscribe', 'hub-support', 'hub-terrier', 'hub-thumbs_up', 'hub-type', 'hub-unprioritized', 'hub-upload', 'hub-user', 'hub-users', 'hub-week'
|
|
717
749
|
] as const
|
|
718
750
|
|
|
719
751
|
export type HubIconName = typeof Names[number]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd"><circle cx="8" cy="24" r="3" fill="currentColor" fill-opacity="0.33" stroke="currentColor" stroke-linejoin="round" stroke-width="2"/><circle cx="16" cy="8" r="3" fill="currentColor" fill-opacity="0.33" stroke="currentColor" stroke-linejoin="round" stroke-width="2"/><path stroke="currentColor" stroke-linejoin="round" stroke-width="2" d="M8 21v-3a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v5"/><path fill="currentColor" d="M15 11h2v5h-2zM19.415 23.65l3.826 4.464a1 1 0 0 0 1.518 0l3.826-4.463a1 1 0 0 0-.76-1.651h-7.65a1 1 0 0 0-.76 1.65Z"/></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd"><circle cx="8" cy="24" r="3" fill="currentColor" fill-opacity="0.33" stroke="currentColor" stroke-linejoin="round" stroke-width="2"/><circle cx="24" cy="24" r="3" fill="currentColor" fill-opacity="0.33" stroke="currentColor" stroke-linejoin="round" stroke-width="2"/><path stroke="currentColor" stroke-linejoin="round" stroke-width="2" d="M8 21v-3a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3"/><path fill="currentColor" d="M15 9h2v7h-2z"/><path fill="currentColor" d="m11.415 8.35 3.826-4.464a1 1 0 0 1 1.518 0l3.826 4.463a1 1 0 0 1-.76 1.651h-7.65a1 1 0 0 1-.76-1.65Z"/></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd"><circle cx="16" cy="8" r="3" fill="currentColor" fill-opacity="0.33" stroke="currentColor" stroke-linejoin="round" stroke-width="2"/><circle cx="8" cy="24" r="3" fill="currentColor" fill-opacity="0.33" stroke="currentColor" stroke-linejoin="round" stroke-width="2"/><circle cx="24" cy="24" r="5" fill="currentColor" fill-opacity="0.33" stroke="currentColor" stroke-linejoin="round" stroke-width="2"/><path stroke="currentColor" stroke-linejoin="round" stroke-width="2" d="M8 21v-3a2 2 0 0 1 2-2h12.023a2 2 0 0 1 2 1.977l.011.977"/><path fill="currentColor" d="M15 11h2v5h-2zM24 21a1 1 0 0 1 1 1v.999L26 23a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0v-1h-1a1 1 0 0 1 0-2h1v-1a1 1 0 0 1 1-1Z"/></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd"><circle cx="8" cy="24" r="3" fill="currentColor" fill-opacity="0.33" stroke="currentColor" stroke-linejoin="round" stroke-width="2"/><circle cx="24" cy="24" r="3" fill="currentColor" fill-opacity="0.33" stroke="currentColor" stroke-linejoin="round" stroke-width="2"/><path stroke="currentColor" stroke-linejoin="round" stroke-width="2" d="M8 21v-3a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3"/><path fill="currentColor" d="M15 13h2v3h-2z"/><circle cx="16" cy="8" r="5" fill="currentColor" fill-opacity="0.33" stroke="currentColor" stroke-linejoin="round" stroke-width="2"/><path fill="currentColor" d="M16 5a1 1 0 0 1 1 1v.999L18 7a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V9h-1a1 1 0 0 1 0-2h1V6a1 1 0 0 1 1-1Z"/></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd"><circle cx="8" cy="24" r="3" fill="#000" fill-opacity=".25" stroke="#000" stroke-linejoin="round" stroke-width="2"/><circle cx="16" cy="8" r="3" fill="#000" fill-opacity=".25" stroke="#000" stroke-linejoin="round" stroke-width="2"/><path stroke="#000" stroke-linejoin="round" stroke-width="2" d="M8 21v-3a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v5"/><path fill="#000" d="M15 11h2v5h-2zM19.415 23.65l3.826 4.464a1 1 0 0 0 1.518 0l3.826-4.463a1 1 0 0 0-.76-1.651h-7.65a1 1 0 0 0-.76 1.65Z"/></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd"><circle cx="8" cy="24" r="3" fill="#000" fill-opacity=".25" stroke="#000" stroke-linejoin="round" stroke-width="2"/><circle cx="24" cy="24" r="3" fill="#000" fill-opacity=".25" stroke="#000" stroke-linejoin="round" stroke-width="2"/><path stroke="#000" stroke-linejoin="round" stroke-width="2" d="M8 21v-3a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3"/><path fill="#000" d="M15 9h2v7h-2z"/><path fill="#000" d="m11.415 8.35 3.826-4.464a1 1 0 0 1 1.518 0l3.826 4.463a1 1 0 0 1-.76 1.651h-7.65a1 1 0 0 1-.76-1.65Z"/></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd"><circle cx="16" cy="8" r="3" fill="#000" fill-opacity=".25" stroke="#000" stroke-linejoin="round" stroke-width="2"/><circle cx="8" cy="24" r="3" fill="#000" fill-opacity=".25" stroke="#000" stroke-linejoin="round" stroke-width="2"/><circle cx="24" cy="24" r="5" fill="#000" fill-opacity=".25" stroke="#000" stroke-linejoin="round" stroke-width="2"/><path stroke="#000" stroke-linejoin="round" stroke-width="2" d="M8 21v-3a2 2 0 0 1 2-2h12.023a2 2 0 0 1 2 1.977l.011.977"/><path fill="#000" d="M15 11h2v5h-2zM24 21a1 1 0 0 1 1 1v.999L26 23a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0v-1h-1a1 1 0 0 1 0-2h1v-1a1 1 0 0 1 1-1Z"/></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd"><circle cx="8" cy="24" r="3" fill="#000" fill-opacity=".25" stroke="#000" stroke-linejoin="round" stroke-width="2"/><circle cx="24" cy="24" r="3" fill="#000" fill-opacity=".25" stroke="#000" stroke-linejoin="round" stroke-width="2"/><path stroke="#000" stroke-linejoin="round" stroke-width="2" d="M8 21v-3a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3"/><path fill="#000" d="M15 13h2v3h-2z"/><circle cx="16" cy="8" r="5" fill="#000" fill-opacity=".25" stroke="#000" stroke-linejoin="round" stroke-width="2"/><path fill="#000" d="M16 5a1 1 0 0 1 1 1v.999L18 7a1 1 0 0 1 0 2h-1v1a1 1 0 0 1-2 0V9h-1a1 1 0 0 1 0-2h1V6a1 1 0 0 1 1-1Z"/></g></svg>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<title>icon-existing_child</title>
|
|
4
|
+
<g id="icon-existing_child" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
5
|
+
<circle id="Oval-Copy" stroke="#000000" stroke-width="2" fill-opacity="0.25" fill="#000000" stroke-linejoin="round" cx="8" cy="24" r="3"></circle>
|
|
6
|
+
<circle id="Oval-Copy-2" stroke="#000000" stroke-width="2" fill-opacity="0.25" fill="#000000" stroke-linejoin="round" cx="16" cy="8" r="3"></circle>
|
|
7
|
+
<path d="M8,21 L8,18 C8,16.8954305 8.8954305,16 10,16 L22,16 C23.1045695,16 24,16.8954305 24,18 L24,23 L24,23" id="Path-100" stroke="#000000" stroke-width="2" stroke-linejoin="round"></path>
|
|
8
|
+
<rect id="Rectangle" fill="#000000" x="15" y="11" width="2" height="5"></rect>
|
|
9
|
+
<path d="M22.1507914,20.914964 L26.6142006,24.7407434 C27.0335265,25.1001655 27.082088,25.7314655 26.7226659,26.1507914 C26.6893412,26.1896702 26.6530794,26.2259319 26.6142006,26.2592566 L22.1507914,30.085036 C21.7314655,30.4444581 21.1001655,30.3958966 20.7407434,29.9765707 C20.5853922,29.7953276 20.5,29.5644906 20.5,29.3257794 L20.5,21.6742206 C20.5,21.1219359 20.9477153,20.6742206 21.5,20.6742206 C21.7387113,20.6742206 21.9695483,20.7596128 22.1507914,20.914964 Z" id="Path-101" fill="#000000" transform="translate(24, 25.5) scale(1, -1) rotate(-90) translate(-24, -25.5)"></path>
|
|
10
|
+
</g>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<title>icon-existing_parent</title>
|
|
4
|
+
<g id="icon-existing_parent" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
5
|
+
<circle id="Oval-Copy" stroke="#000000" stroke-width="2" fill-opacity="0.25" fill="#000000" stroke-linejoin="round" cx="8" cy="24" r="3"></circle>
|
|
6
|
+
<circle id="Oval-Copy-2" stroke="#000000" stroke-width="2" fill-opacity="0.25" fill="#000000" stroke-linejoin="round" cx="24" cy="24" r="3"></circle>
|
|
7
|
+
<path d="M8,21 L8,18 C8,16.8954305 8.8954305,16 10,16 L22,16 C23.1045695,16 24,16.8954305 24,18 L24,21 L24,21" id="Path-100" stroke="#000000" stroke-width="2" stroke-linejoin="round"></path>
|
|
8
|
+
<rect id="Rectangle" fill="#000000" x="15" y="9" width="2" height="7"></rect>
|
|
9
|
+
<path d="M14.1507914,1.91496403 L18.6142006,5.7407434 C19.0335265,6.10016555 19.082088,6.73146553 18.7226659,7.15079137 C18.6893412,7.18967018 18.6530794,7.22593191 18.6142006,7.2592566 L14.1507914,11.085036 C13.7314655,11.4444581 13.1001655,11.3958966 12.7407434,10.9765707 C12.5853922,10.7953276 12.5,10.5644906 12.5,10.3257794 L12.5,2.67422064 C12.5,2.12193589 12.9477153,1.67422064 13.5,1.67422064 C13.7387113,1.67422064 13.9695483,1.7596128 14.1507914,1.91496403 Z" id="Path-101" fill="#000000" transform="translate(16, 6.5) rotate(-90) translate(-16, -6.5)"></path>
|
|
10
|
+
</g>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<title>icon-new-child</title>
|
|
4
|
+
<g id="icon-new-child" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
5
|
+
<circle id="Oval" stroke="#000000" stroke-width="2" fill-opacity="0.25" fill="#000000" stroke-linejoin="round" cx="16" cy="8" r="3"></circle>
|
|
6
|
+
<circle id="Oval-Copy" stroke="#000000" stroke-width="2" fill-opacity="0.25" fill="#000000" stroke-linejoin="round" cx="8" cy="24" r="3"></circle>
|
|
7
|
+
<circle id="Oval-Copy-2" stroke="#000000" stroke-width="2" fill-opacity="0.25" fill="#000000" stroke-linejoin="round" cx="24" cy="24" r="5"></circle>
|
|
8
|
+
<path d="M8,21 L8,18 C8,16.8954305 8.8954305,16 10,16 L22.0231969,16 C23.1186657,16 24.0102814,16.8812743 24.0230608,17.9766686 L24.0344614,18.9538745 L24.0344614,18.9538745" id="Path-100" stroke="#000000" stroke-width="2" stroke-linejoin="round"></path>
|
|
9
|
+
<rect id="Rectangle" fill="#000000" x="15" y="11" width="2" height="5"></rect>
|
|
10
|
+
<path d="M24,21 C24.5522847,21 25,21.4477153 25,22 L25,22.999 L26,23 C26.5522847,23 27,23.4477153 27,24 C27,24.5522847 26.5522847,25 26,25 L25,25 L25,26 C25,26.5522847 24.5522847,27 24,27 C23.4477153,27 23,26.5522847 23,26 L23,25 L22,25 C21.4477153,25 21,24.5522847 21,24 C21,23.4477153 21.4477153,23 22,23 L23,23 L23,22 C23,21.4477153 23.4477153,21 24,21 Z" id="Combined-Shape" fill="#000000"></path>
|
|
11
|
+
</g>
|
|
12
|
+
</svg>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<title>icon-new_parent</title>
|
|
4
|
+
<g id="icon-new_parent" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
5
|
+
<circle id="Oval-Copy" stroke="#000000" stroke-width="2" fill-opacity="0.25" fill="#000000" stroke-linejoin="round" cx="8" cy="24" r="3"></circle>
|
|
6
|
+
<circle id="Oval-Copy-2" stroke="#000000" stroke-width="2" fill-opacity="0.25" fill="#000000" stroke-linejoin="round" cx="24" cy="24" r="3"></circle>
|
|
7
|
+
<path d="M8,21 L8,18 C8,16.8954305 8.8954305,16 10,16 L22,16 C23.1045695,16 24,16.8954305 24,18 L24,21 L24,21" id="Path-100" stroke="#000000" stroke-width="2" stroke-linejoin="round"></path>
|
|
8
|
+
<rect id="Rectangle" fill="#000000" x="15" y="13" width="2" height="3"></rect>
|
|
9
|
+
<circle id="Oval-Copy-2" stroke="#000000" stroke-width="2" fill-opacity="0.25" fill="#000000" stroke-linejoin="round" cx="16" cy="8" r="5"></circle>
|
|
10
|
+
<path d="M16,5 C16.5522847,5 17,5.44771525 17,6 L17,6.999 L18,7 C18.5522847,7 19,7.44771525 19,8 C19,8.55228475 18.5522847,9 18,9 L17,9 L17,10 C17,10.5522847 16.5522847,11 16,11 C15.4477153,11 15,10.5522847 15,10 L15,9 L14,9 C13.4477153,9 13,8.55228475 13,8 C13,7.44771525 13.4477153,7 14,7 L15,7 L15,6 C15,5.44771525 15.4477153,5 16,5 Z" id="Combined-Shape" fill="#000000"></path>
|
|
11
|
+
</g>
|
|
12
|
+
</svg>
|