terrier-engine 4.0.21 → 4.3.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/app.ts +37 -27
- package/dropdowns.ts +27 -22
- package/forms.ts +33 -0
- package/fragments.ts +39 -37
- package/gen/hub-icons.ts +697 -0
- package/glyps.ts +2 -2
- package/images/icons/active.svg +1 -0
- package/images/icons/admin.svg +1 -0
- package/images/icons/archive.svg +1 -0
- package/images/icons/arrow_down.svg +1 -0
- package/images/icons/arrow_left.svg +1 -0
- package/images/icons/arrow_right.svg +1 -0
- package/images/icons/arrow_up.svg +1 -0
- package/images/icons/assign.svg +1 -0
- package/images/icons/attachment.svg +1 -0
- package/images/icons/back.svg +1 -0
- package/images/icons/badge.svg +1 -0
- package/images/icons/board.svg +1 -0
- package/images/icons/branch.svg +1 -0
- package/images/icons/bug.svg +1 -0
- package/images/icons/calculator.svg +1 -0
- package/images/icons/checkmark.svg +1 -0
- package/images/icons/close.svg +1 -0
- package/images/icons/clypboard.svg +1 -0
- package/images/icons/comment.svg +1 -0
- package/images/icons/complete.svg +1 -0
- package/images/icons/dashboard.svg +1 -0
- package/images/icons/data_pull.svg +1 -0
- package/images/icons/data_update.svg +1 -0
- package/images/icons/database.svg +1 -0
- package/images/icons/day.svg +1 -0
- package/images/icons/delete.svg +1 -0
- package/images/icons/documentation.svg +1 -0
- package/images/icons/edit.svg +1 -0
- package/images/icons/feature.svg +1 -0
- package/images/icons/flex.svg +1 -0
- package/images/icons/forward.svg +1 -0
- package/images/icons/github.svg +1 -0
- package/images/icons/history.svg +1 -0
- package/images/icons/home.svg +1 -0
- package/images/icons/image.svg +1 -0
- package/images/icons/inbox.svg +1 -0
- package/images/icons/info.svg +1 -0
- package/images/icons/issue.svg +1 -0
- package/images/icons/lane.svg +1 -0
- package/images/icons/lane_asap.svg +1 -0
- package/images/icons/lane_days.svg +1 -0
- package/images/icons/lane_hours.svg +1 -0
- package/images/icons/lane_weeks.svg +1 -0
- package/images/icons/lanes_board.svg +1 -0
- package/images/icons/level_complete.svg +1 -0
- package/images/icons/level_highway.svg +1 -0
- package/images/icons/level_on_ramp.svg +1 -0
- package/images/icons/level_parking.svg +1 -0
- package/images/icons/minus.svg +1 -0
- package/images/icons/night.svg +1 -0
- package/images/icons/origin.svg +1 -0
- package/images/icons/pending.svg +1 -0
- package/images/icons/plus.svg +1 -0
- package/images/icons/post.svg +1 -0
- package/images/icons/pr_closed.svg +1 -0
- package/images/icons/pr_merged.svg +1 -0
- package/images/icons/pr_open.svg +1 -0
- package/images/icons/prioritized.svg +1 -0
- package/images/icons/project.svg +1 -0
- package/images/icons/question.svg +1 -0
- package/images/icons/reaction.svg +1 -0
- package/images/icons/recent.svg +1 -0
- package/images/icons/refresh.svg +1 -0
- package/images/icons/request.svg +1 -0
- package/images/icons/settings.svg +1 -0
- package/images/icons/status.svg +1 -0
- package/images/icons/step_deploy.svg +1 -0
- package/images/icons/step_develop.svg +1 -0
- package/images/icons/step_investigate.svg +1 -0
- package/images/icons/step_review.svg +1 -0
- package/images/icons/step_test.svg +1 -0
- package/images/icons/steps.svg +1 -0
- package/images/icons/steps_board.svg +1 -0
- package/images/icons/subscribe.svg +1 -0
- package/images/icons/support.svg +1 -0
- package/images/icons/terrier.svg +1 -0
- package/images/icons/thumbs_up.svg +1 -0
- package/images/icons/type.svg +1 -0
- package/images/icons/unprioritized.svg +1 -0
- package/images/icons/upload.svg +1 -0
- package/images/icons/user.svg +1 -0
- package/images/icons/users.svg +1 -0
- package/images/optimized/icon-active.svg +1 -0
- package/images/optimized/icon-admin.svg +1 -0
- package/images/optimized/icon-archive.svg +1 -0
- package/images/optimized/icon-arrow_down.svg +1 -0
- package/images/optimized/icon-arrow_left.svg +1 -0
- package/images/optimized/icon-arrow_right.svg +1 -0
- package/images/optimized/icon-arrow_up.svg +1 -0
- package/images/optimized/icon-assign.svg +1 -0
- package/images/optimized/icon-attachment.svg +1 -0
- package/images/optimized/icon-back.svg +1 -0
- package/images/optimized/icon-badge.svg +1 -0
- package/images/optimized/icon-board.svg +1 -0
- package/images/optimized/icon-branch.svg +1 -0
- package/images/optimized/icon-bug.svg +1 -0
- package/images/optimized/icon-calculator.svg +1 -0
- package/images/optimized/icon-checkmark.svg +1 -0
- package/images/optimized/icon-close.svg +1 -0
- package/images/optimized/icon-clypboard.svg +1 -0
- package/images/optimized/icon-comment.svg +1 -0
- package/images/optimized/icon-complete.svg +1 -0
- package/images/optimized/icon-dashboard.svg +1 -0
- package/images/optimized/icon-data_pull.svg +1 -0
- package/images/optimized/icon-data_update.svg +1 -0
- package/images/optimized/icon-database.svg +1 -0
- package/images/optimized/icon-day.svg +1 -0
- package/images/optimized/icon-delete.svg +1 -0
- package/images/optimized/icon-documentation.svg +1 -0
- package/images/optimized/icon-edit.svg +1 -0
- package/images/optimized/icon-feature.svg +1 -0
- package/images/optimized/icon-flex.svg +1 -0
- package/images/optimized/icon-forward.svg +1 -0
- package/images/optimized/icon-github.svg +1 -0
- package/images/optimized/icon-history.svg +1 -0
- package/images/optimized/icon-home.svg +1 -0
- package/images/optimized/icon-image.svg +1 -0
- package/images/optimized/icon-inbox.svg +1 -0
- package/images/optimized/icon-info.svg +1 -0
- package/images/optimized/icon-issue.svg +1 -0
- package/images/optimized/icon-lane.svg +1 -0
- package/images/optimized/icon-lane_asap.svg +1 -0
- package/images/optimized/icon-lane_days.svg +1 -0
- package/images/optimized/icon-lane_hours.svg +1 -0
- package/images/optimized/icon-lane_weeks.svg +1 -0
- package/images/optimized/icon-lanes_board.svg +1 -0
- package/images/optimized/icon-level_complete.svg +1 -0
- package/images/optimized/icon-level_highway.svg +1 -0
- package/images/optimized/icon-level_on_ramp.svg +1 -0
- package/images/optimized/icon-level_parking.svg +1 -0
- package/images/optimized/icon-minus.svg +1 -0
- package/images/optimized/icon-night.svg +1 -0
- package/images/optimized/icon-origin.svg +1 -0
- package/images/optimized/icon-pending.svg +1 -0
- package/images/optimized/icon-plus.svg +1 -0
- package/images/optimized/icon-post.svg +1 -0
- package/images/optimized/icon-pr_closed.svg +1 -0
- package/images/optimized/icon-pr_merged.svg +1 -0
- package/images/optimized/icon-pr_open.svg +1 -0
- package/images/optimized/icon-prioritized.svg +1 -0
- package/images/optimized/icon-project.svg +1 -0
- package/images/optimized/icon-question.svg +1 -0
- package/images/optimized/icon-reaction.svg +1 -0
- package/images/optimized/icon-recent.svg +1 -0
- package/images/optimized/icon-refresh.svg +1 -0
- package/images/optimized/icon-request.svg +1 -0
- package/images/optimized/icon-settings.svg +1 -0
- package/images/optimized/icon-status.svg +1 -0
- package/images/optimized/icon-step_deploy.svg +1 -0
- package/images/optimized/icon-step_develop.svg +1 -0
- package/images/optimized/icon-step_investigate.svg +1 -0
- package/images/optimized/icon-step_review.svg +1 -0
- package/images/optimized/icon-step_test.svg +1 -0
- package/images/optimized/icon-steps.svg +1 -0
- package/images/optimized/icon-steps_board.svg +1 -0
- package/images/optimized/icon-subscribe.svg +1 -0
- package/images/optimized/icon-support.svg +1 -0
- package/images/optimized/icon-terrier.svg +1 -0
- package/images/optimized/icon-thumbs_up.svg +1 -0
- package/images/optimized/icon-type.svg +1 -0
- package/images/optimized/icon-unprioritized.svg +1 -0
- package/images/optimized/icon-upload.svg +1 -0
- package/images/optimized/icon-user.svg +1 -0
- package/images/optimized/icon-users.svg +1 -0
- package/images/optimized/terrier-hub-favicon.svg +1 -0
- package/images/optimized/terrier-hub-icon-dark.svg +1 -0
- package/images/optimized/terrier-hub-icon-light.svg +1 -0
- package/images/optimized/terrier-hub-loader.svg +1 -0
- package/images/optimized/terrier-hub-logo-dark.svg +1 -0
- package/images/optimized/terrier-hub-logo-light.svg +1 -0
- package/images/raw/icon-active.svg +8 -0
- package/images/raw/icon-admin.svg +9 -0
- package/images/raw/icon-archive.svg +9 -0
- package/images/raw/icon-arrow_down.svg +7 -0
- package/images/raw/icon-arrow_left.svg +7 -0
- package/images/raw/icon-arrow_right.svg +7 -0
- package/images/raw/icon-arrow_up.svg +7 -0
- package/images/raw/icon-assign.svg +8 -0
- package/images/raw/icon-attachment.svg +7 -0
- package/images/raw/icon-back.svg +7 -0
- package/images/raw/icon-badge.svg +10 -0
- package/images/raw/icon-board.svg +20 -0
- package/images/raw/icon-branch.svg +11 -0
- package/images/raw/icon-bug.svg +8 -0
- package/images/raw/icon-calculator.svg +31 -0
- package/images/raw/icon-checkmark.svg +8 -0
- package/images/raw/icon-close.svg +8 -0
- package/images/raw/icon-clypboard.svg +9 -0
- package/images/raw/icon-comment.svg +12 -0
- package/images/raw/icon-complete.svg +8 -0
- package/images/raw/icon-dashboard.svg +18 -0
- package/images/raw/icon-data_pull.svg +9 -0
- package/images/raw/icon-data_update.svg +9 -0
- package/images/raw/icon-database.svg +10 -0
- package/images/raw/icon-day.svg +19 -0
- package/images/raw/icon-delete.svg +11 -0
- package/images/raw/icon-documentation.svg +21 -0
- package/images/raw/icon-edit.svg +11 -0
- package/images/raw/icon-feature.svg +7 -0
- package/images/raw/icon-flex.svg +6 -0
- package/images/raw/icon-forward.svg +7 -0
- package/images/raw/icon-github.svg +8 -0
- package/images/raw/icon-history.svg +12 -0
- package/images/raw/icon-home.svg +8 -0
- package/images/raw/icon-image.svg +9 -0
- package/images/raw/icon-inbox.svg +9 -0
- package/images/raw/icon-info.svg +11 -0
- package/images/raw/icon-issue.svg +10 -0
- package/images/raw/icon-lane.svg +9 -0
- package/images/raw/icon-lane_asap.svg +9 -0
- package/images/raw/icon-lane_days.svg +11 -0
- package/images/raw/icon-lane_hours.svg +8 -0
- package/images/raw/icon-lane_weeks.svg +10 -0
- package/images/raw/icon-lanes_board.svg +10 -0
- package/images/raw/icon-level_complete.svg +8 -0
- package/images/raw/icon-level_highway.svg +11 -0
- package/images/raw/icon-level_on_ramp.svg +8 -0
- package/images/raw/icon-level_parking.svg +10 -0
- package/images/raw/icon-minus.svg +8 -0
- package/images/raw/icon-night.svg +9 -0
- package/images/raw/icon-origin.svg +11 -0
- package/images/raw/icon-pending.svg +10 -0
- package/images/raw/icon-plus.svg +8 -0
- package/images/raw/icon-post.svg +12 -0
- package/images/raw/icon-pr_closed.svg +15 -0
- package/images/raw/icon-pr_merged.svg +11 -0
- package/images/raw/icon-pr_open.svg +12 -0
- package/images/raw/icon-prioritized.svg +11 -0
- package/images/raw/icon-project.svg +24 -0
- package/images/raw/icon-question.svg +9 -0
- package/images/raw/icon-reaction.svg +6 -0
- package/images/raw/icon-recent.svg +12 -0
- package/images/raw/icon-refresh.svg +11 -0
- package/images/raw/icon-request.svg +10 -0
- package/images/raw/icon-settings.svg +11 -0
- package/images/raw/icon-status.svg +8 -0
- package/images/raw/icon-step_deploy.svg +15 -0
- package/images/raw/icon-step_develop.svg +12 -0
- package/images/raw/icon-step_investigate.svg +14 -0
- package/images/raw/icon-step_review.svg +11 -0
- package/images/raw/icon-step_test.svg +14 -0
- package/images/raw/icon-steps.svg +18 -0
- package/images/raw/icon-steps_board.svg +19 -0
- package/images/raw/icon-subscribe.svg +10 -0
- package/images/raw/icon-support.svg +14 -0
- package/images/raw/icon-terrier.svg +7 -0
- package/images/raw/icon-thumbs_up.svg +1 -0
- package/images/raw/icon-type.svg +15 -0
- package/images/raw/icon-unprioritized.svg +10 -0
- package/images/raw/icon-upload.svg +8 -0
- package/images/raw/icon-user.svg +9 -0
- package/images/raw/icon-users.svg +14 -0
- package/images/raw/terrier-hub-favicon-alert.png +0 -0
- package/images/raw/terrier-hub-favicon-dark.png +0 -0
- package/images/raw/terrier-hub-favicon.png +0 -0
- package/images/raw/terrier-hub-favicon.svg +29 -0
- package/images/raw/terrier-hub-icon-dark.svg +23 -0
- package/images/raw/terrier-hub-icon-light.png +0 -0
- package/images/raw/terrier-hub-icon-light.svg +23 -0
- package/images/raw/terrier-hub-loader.svg +54 -0
- package/images/raw/terrier-hub-logo-dark.svg +27 -0
- package/images/raw/terrier-hub-logo-light.png +0 -0
- package/images/raw/terrier-hub-logo-light.svg +28 -0
- package/lightbox.ts +9 -22
- package/loading.ts +5 -6
- package/modals.ts +8 -19
- package/overlays.ts +100 -33
- package/package.json +1 -1
- package/parts/content-part.ts +187 -0
- package/parts/not-found-page.ts +20 -0
- package/parts/page-part.ts +189 -0
- package/parts/panel-part.ts +40 -0
- package/parts/terrier-form-part.ts +20 -0
- package/parts/terrier-part.ts +89 -0
- package/schema.ts +28 -1
- package/tabs.ts +164 -0
- package/theme.ts +41 -12
- package/toasts.ts +10 -10
- package/tooltips.ts +2 -2
- package/parts.ts +0 -485
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {Part} from "tuff-core/parts"
|
|
2
|
+
import {TerrierApp} from "../app"
|
|
3
|
+
import Loading from "../loading"
|
|
4
|
+
import Theme from "../theme"
|
|
5
|
+
import Toasts, {ToastOptions} from "../toasts"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Base class for ALL parts in a Terrier application.
|
|
9
|
+
*/
|
|
10
|
+
export default abstract class TerrierPart<TState> extends Part<TState> {
|
|
11
|
+
|
|
12
|
+
get app(): TerrierApp<any> {
|
|
13
|
+
return this.root as TerrierApp<any>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get theme(): Theme {
|
|
17
|
+
return this.app.theme
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// Loading
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* This can be overloaded if the loading overlay should go
|
|
24
|
+
* somewhere other than the part's root element.
|
|
25
|
+
*/
|
|
26
|
+
getLoadingContainer(): Element | null | undefined {
|
|
27
|
+
return this.element
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Shows the loading animation on top of the part.
|
|
33
|
+
*/
|
|
34
|
+
startLoading() {
|
|
35
|
+
const elem = this.getLoadingContainer()
|
|
36
|
+
if (!elem) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
Loading.showOverlay(elem, this.theme)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Removes the loading animation from the part.
|
|
44
|
+
*/
|
|
45
|
+
stopLoading() {
|
|
46
|
+
const elem = this.getLoadingContainer()
|
|
47
|
+
if (!elem) {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
Loading.removeOverlay(elem)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Shows the loading overlay until the given function completes (either returns successfully or throws an exception)
|
|
55
|
+
* @param func
|
|
56
|
+
*/
|
|
57
|
+
showLoading(func: () => void): void
|
|
58
|
+
showLoading(func: () => Promise<void>): Promise<void>
|
|
59
|
+
showLoading(func: () => void | Promise<void>): void | Promise<void> {
|
|
60
|
+
this.startLoading()
|
|
61
|
+
let stopImmediately = true
|
|
62
|
+
try {
|
|
63
|
+
const res = func()
|
|
64
|
+
if (res) {
|
|
65
|
+
stopImmediately = false
|
|
66
|
+
return res.finally(() => {
|
|
67
|
+
this.stopLoading()
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
} finally {
|
|
71
|
+
if (stopImmediately) {
|
|
72
|
+
this.stopLoading()
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
/// Toasts
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Shows a toast message in a bubble in the upper right corner.
|
|
82
|
+
* @param message the message text
|
|
83
|
+
* @param options
|
|
84
|
+
*/
|
|
85
|
+
showToast(message: string, options: ToastOptions) {
|
|
86
|
+
Toasts.show(message, options, this.theme)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
}
|
package/schema.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import Api from "./api"
|
|
2
|
+
import inflection from "inflection";
|
|
2
3
|
|
|
3
4
|
////////////////////////////////////////////////////////////////////////////////
|
|
4
5
|
// Schema Definitions
|
|
@@ -60,8 +61,34 @@ async function get(): Promise<SchemaDef> {
|
|
|
60
61
|
return res.schema
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
|
|
65
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
66
|
+
// Utilities
|
|
67
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Generated a string used to display a `BelongsToDef` to the user.
|
|
71
|
+
* If the name differs from the model, the name will be included in parentheses.
|
|
72
|
+
* @param belongsTo
|
|
73
|
+
*/
|
|
74
|
+
function belongsToDisplay(belongsTo: BelongsToDef): string {
|
|
75
|
+
if (belongsTo.name != inflection.singularize(inflection.tableize(belongsTo.model))) {
|
|
76
|
+
// the model is different than the name of the association
|
|
77
|
+
return `${belongsTo.model} (${belongsTo.name})`
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
return belongsTo.model
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
86
|
+
// Export
|
|
87
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
88
|
+
|
|
63
89
|
const Schema = {
|
|
64
|
-
get
|
|
90
|
+
get,
|
|
91
|
+
belongsToDisplay
|
|
65
92
|
}
|
|
66
93
|
|
|
67
94
|
export default Schema
|
package/tabs.ts
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import {Logger} from "tuff-core/logging"
|
|
2
|
+
import {typedKey} from "tuff-core/messages"
|
|
3
|
+
import {Part, PartParent, PartTag, StatelessPart} from "tuff-core/parts"
|
|
4
|
+
import TerrierPart from "./parts/terrier-part"
|
|
5
|
+
import {Action, IconName} from "./theme";
|
|
6
|
+
|
|
7
|
+
const log = new Logger("Tabs")
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Parameters used for initial tab creation
|
|
11
|
+
*/
|
|
12
|
+
export type TabParams = {
|
|
13
|
+
key: string
|
|
14
|
+
title: string
|
|
15
|
+
icon?: IconName
|
|
16
|
+
state?: 'enabled' | 'disabled' | 'hidden'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Properties required to render a tab
|
|
21
|
+
*/
|
|
22
|
+
export type TabDefinition = TabParams & {
|
|
23
|
+
part: Part<unknown>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const Sides = ['top', 'left', 'bottom', 'right'] as const
|
|
27
|
+
|
|
28
|
+
export type TabSide = typeof Sides[number]
|
|
29
|
+
|
|
30
|
+
export type TabContainerState = {
|
|
31
|
+
side: TabSide
|
|
32
|
+
currentTab? : string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class TabContainerPart extends TerrierPart<TabContainerState> {
|
|
36
|
+
|
|
37
|
+
private tabs = {} as Record<string, TabDefinition>
|
|
38
|
+
changeTabKey = typedKey<{ tabKey: string }>()
|
|
39
|
+
changeSideKey = typedKey<{ side: TabSide }>()
|
|
40
|
+
|
|
41
|
+
async init() {
|
|
42
|
+
this.onClick(this.changeTabKey, m => {
|
|
43
|
+
log.info(`Clicked on tab ${m.data.tabKey}`)
|
|
44
|
+
this.showTab(m.data.tabKey)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
this.onChange(this.changeSideKey, m => {
|
|
48
|
+
log.info(`Change tab side: ${m.data.side}`)
|
|
49
|
+
this.state.side = m.data.side
|
|
50
|
+
this.dirty()
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Adds or overwrites an existing tab.
|
|
56
|
+
* @param tab initial parameters for the tab
|
|
57
|
+
* @param constructor constructor for the part that will make up the contents of the tab
|
|
58
|
+
* @param state initial state for the content part
|
|
59
|
+
*/
|
|
60
|
+
upsertTab<PartType extends Part<PartStateType>, PartStateType, InferredPartStateType extends PartStateType>(
|
|
61
|
+
tab: TabParams,
|
|
62
|
+
constructor: { new(p: PartParent, id: string, state: PartStateType): PartType; },
|
|
63
|
+
state: InferredPartStateType
|
|
64
|
+
): PartType {
|
|
65
|
+
const existingTab = this.tabs[tab.key] ?? {}
|
|
66
|
+
this.tabs[tab.key] = Object.assign(existingTab, {state: 'enabled'}, tab)
|
|
67
|
+
const part = this.makePart(constructor, state)
|
|
68
|
+
existingTab.part = part
|
|
69
|
+
this.dirty()
|
|
70
|
+
return part
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Updates an existing tab with the given params
|
|
75
|
+
* @param tab
|
|
76
|
+
*/
|
|
77
|
+
updateTab(tab: TabParams): void {
|
|
78
|
+
const existingTab = this.tabs[tab.key]
|
|
79
|
+
if (!existingTab) throw `Tab with key '${tab.key}' does not exist!`
|
|
80
|
+
Object.assign(existingTab, tab)
|
|
81
|
+
this.dirty()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Changes this tab container to show the tab with the given key
|
|
86
|
+
* @param tabKey
|
|
87
|
+
*/
|
|
88
|
+
showTab(tabKey: string) {
|
|
89
|
+
if (!(tabKey in this.tabs)) {
|
|
90
|
+
throw `Unknown tab key ${tabKey}`
|
|
91
|
+
}
|
|
92
|
+
if (this.tabs[tabKey].state != 'enabled') return // tab exists but is not enabled
|
|
93
|
+
if (this.state.currentTab === tabKey) return // tab is already selected
|
|
94
|
+
|
|
95
|
+
this.state.currentTab = tabKey
|
|
96
|
+
this.dirty()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
_beforeAction?: Action
|
|
101
|
+
|
|
102
|
+
setBeforeAction(action: Action) {
|
|
103
|
+
this._beforeAction = action
|
|
104
|
+
this.dirty()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
_afterAction?: Action
|
|
108
|
+
|
|
109
|
+
setAfterAction(action: Action) {
|
|
110
|
+
this._afterAction = action
|
|
111
|
+
this.dirty()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
render(parent: PartTag) {
|
|
117
|
+
let currentTabKey = this.state.currentTab
|
|
118
|
+
if (!currentTabKey) {
|
|
119
|
+
log.debug("no current tab specified, selecting first enabled tab")
|
|
120
|
+
currentTabKey = Object.values(this.tabs).find(t => t.state == 'enabled')?.key
|
|
121
|
+
}
|
|
122
|
+
parent.div('tt-tab-container', this.state.side, container => {
|
|
123
|
+
container.div('.tt-flex.tt-tab-list', tabList => {
|
|
124
|
+
if (this._beforeAction) {
|
|
125
|
+
this.theme.renderActions(tabList, [this._beforeAction], {defaultClass: 'action'})
|
|
126
|
+
}
|
|
127
|
+
for (const tab of Object.values(this.tabs)) {
|
|
128
|
+
if (tab.state == 'hidden') continue
|
|
129
|
+
|
|
130
|
+
tabList.a('.tab', a => {
|
|
131
|
+
a.class(tab.state || 'enabled')
|
|
132
|
+
if (tab.key === currentTabKey) {
|
|
133
|
+
a.class('active')
|
|
134
|
+
}
|
|
135
|
+
if (tab.icon) {
|
|
136
|
+
this.theme.renderIcon(a, tab.icon)
|
|
137
|
+
}
|
|
138
|
+
a.span({text: tab.title})
|
|
139
|
+
a.emitClick(this.changeTabKey, {tabKey: tab.key})
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
if (this._afterAction) {
|
|
143
|
+
tabList.div('.spacer')
|
|
144
|
+
this.theme.renderActions(tabList, [this._afterAction], {defaultClass: 'action'})
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
if (currentTabKey) {
|
|
149
|
+
const currentTabPart = this.tabs[currentTabKey].part
|
|
150
|
+
container.div('.tt-tab-content', panel => {
|
|
151
|
+
panel.part(currentTabPart as StatelessPart)
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const Tabs = {
|
|
160
|
+
TabContainerPart,
|
|
161
|
+
Sides
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export default Tabs
|
package/theme.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {PartTag} from "tuff-core/parts"
|
|
2
2
|
import {messages} from "tuff-core"
|
|
3
|
+
import {GlypName} from "./glyps"
|
|
4
|
+
import HubIcons, {HubIconName} from "./gen/hub-icons"
|
|
3
5
|
|
|
4
6
|
export interface ThemeType {
|
|
5
7
|
readonly icons: string
|
|
@@ -7,6 +9,15 @@ export interface ThemeType {
|
|
|
7
9
|
}
|
|
8
10
|
|
|
9
11
|
|
|
12
|
+
export type IconName = GlypName | HubIconName
|
|
13
|
+
|
|
14
|
+
const ColorNames = [
|
|
15
|
+
'link', 'primary', 'secondary', 'active', 'pending', 'success', 'alert', 'white', 'inactive'
|
|
16
|
+
] as const
|
|
17
|
+
|
|
18
|
+
export type ColorName = typeof ColorNames[number]
|
|
19
|
+
|
|
20
|
+
|
|
10
21
|
/**
|
|
11
22
|
* A combination of a message key and its associated data.
|
|
12
23
|
*/
|
|
@@ -18,10 +29,10 @@ export type Packet = {
|
|
|
18
29
|
/**
|
|
19
30
|
* An action that generates a button or link.
|
|
20
31
|
*/
|
|
21
|
-
export type Action
|
|
32
|
+
export type Action = {
|
|
22
33
|
title?: string
|
|
23
34
|
tooltip?: string
|
|
24
|
-
icon?:
|
|
35
|
+
icon?: IconName
|
|
25
36
|
href?: string
|
|
26
37
|
classes?: string[]
|
|
27
38
|
click?: Packet
|
|
@@ -31,20 +42,38 @@ export type Action<TT extends ThemeType> = {
|
|
|
31
42
|
/**
|
|
32
43
|
* Options to pass to `render` that control how the actions are displayed.
|
|
33
44
|
*/
|
|
34
|
-
export type RenderActionOptions
|
|
35
|
-
iconColor?:
|
|
36
|
-
badgeColor?:
|
|
45
|
+
export type RenderActionOptions = {
|
|
46
|
+
iconColor?: ColorName | null
|
|
47
|
+
badgeColor?: ColorName
|
|
37
48
|
defaultClass?: string
|
|
38
49
|
}
|
|
39
50
|
|
|
40
|
-
export default
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
export default class Theme {
|
|
52
|
+
|
|
53
|
+
renderIcon(parent: PartTag, icon: IconName, color?: ColorName | null): void {
|
|
54
|
+
if (HubIcons.Names.includes(icon as HubIconName)) {
|
|
55
|
+
HubIcons.renderIcon(parent, icon as HubIconName, color)
|
|
56
|
+
}
|
|
57
|
+
else { // a regular font icon
|
|
58
|
+
const classes: string[] = [icon]
|
|
59
|
+
if (color?.length) {
|
|
60
|
+
classes.push(color)
|
|
61
|
+
}
|
|
62
|
+
parent.i().class(...classes)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
44
65
|
|
|
45
|
-
|
|
66
|
+
renderCloseIcon(parent: PartTag, color?: ColorName | null): void {
|
|
67
|
+
const classes = ['glyp-close', 'close']
|
|
68
|
+
if (color?.length) {
|
|
69
|
+
classes.push(color)
|
|
70
|
+
}
|
|
71
|
+
parent.i(...classes)
|
|
72
|
+
}
|
|
46
73
|
|
|
47
|
-
|
|
74
|
+
getLoaderSrc(): string {
|
|
75
|
+
return ""
|
|
76
|
+
}
|
|
48
77
|
|
|
49
78
|
/**
|
|
50
79
|
* Renders one ore more `Action`s into a parent tag.
|
|
@@ -52,7 +81,7 @@ export default abstract class Theme<TT extends ThemeType> {
|
|
|
52
81
|
* @param actions the action or actions to render
|
|
53
82
|
* @param options additional rendering options
|
|
54
83
|
*/
|
|
55
|
-
renderActions(parent: PartTag, actions: Action
|
|
84
|
+
renderActions(parent: PartTag, actions: Action | Action[], options?: RenderActionOptions) {
|
|
56
85
|
if (!Array.isArray(actions)) {
|
|
57
86
|
actions = [actions]
|
|
58
87
|
}
|
package/toasts.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import Theme, {
|
|
1
|
+
import {Logger} from "tuff-core/logging"
|
|
2
|
+
import Html from "tuff-core/html"
|
|
3
|
+
import Theme, {ColorName, IconName} from "./theme"
|
|
4
4
|
|
|
5
5
|
const log = new Logger('Toasts')
|
|
6
6
|
|
|
7
|
-
export type ToastOptions
|
|
8
|
-
color:
|
|
9
|
-
icon?:
|
|
7
|
+
export type ToastOptions = {
|
|
8
|
+
color: ColorName
|
|
9
|
+
icon?: IconName
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -15,21 +15,21 @@ export type ToastOptions<TT extends ThemeType> = {
|
|
|
15
15
|
* @param options
|
|
16
16
|
* @param theme the theme used to render the toast
|
|
17
17
|
*/
|
|
18
|
-
function show
|
|
18
|
+
function show(message: string, options: ToastOptions, theme: Theme) {
|
|
19
19
|
log.info(`Show ${options.color }: ${message}`)
|
|
20
20
|
|
|
21
21
|
// ensure the container exists
|
|
22
22
|
let container = document.getElementById('tt-toasts')
|
|
23
23
|
if (!container) {
|
|
24
24
|
log.debug(`Creating toasts container`)
|
|
25
|
-
container = createElement('div', (div) => {
|
|
26
|
-
div.sel('#tt-toasts.flex.column.padded')
|
|
25
|
+
container = Html.createElement('div', (div) => {
|
|
26
|
+
div.sel('#tt-toasts.tt-flex.column.padded')
|
|
27
27
|
})
|
|
28
28
|
document.body.appendChild(container)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// create the toast element
|
|
32
|
-
const toast = createElement('div', (parent) => {
|
|
32
|
+
const toast = Html.createElement('div', (parent) => {
|
|
33
33
|
parent.class('tt-toast')
|
|
34
34
|
parent.class(options.color)
|
|
35
35
|
if (options?.icon) {
|
package/tooltips.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Logger } from "tuff-core/logging"
|
|
2
|
-
import
|
|
2
|
+
import Html from "tuff-core/html"
|
|
3
3
|
import Overlays from "./overlays"
|
|
4
4
|
|
|
5
5
|
const log = new Logger('Tooltips')
|
|
@@ -11,7 +11,7 @@ function ensureContainer(): HTMLElement {
|
|
|
11
11
|
if (container) {
|
|
12
12
|
return container
|
|
13
13
|
}
|
|
14
|
-
container = createElement('div', div => {
|
|
14
|
+
container = Html.createElement('div', div => {
|
|
15
15
|
div.sel('#tooltip')
|
|
16
16
|
})
|
|
17
17
|
document.body.appendChild(container)
|