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.
Files changed (286) hide show
  1. package/app.ts +37 -27
  2. package/dropdowns.ts +27 -22
  3. package/forms.ts +33 -0
  4. package/fragments.ts +39 -37
  5. package/gen/hub-icons.ts +697 -0
  6. package/glyps.ts +2 -2
  7. package/images/icons/active.svg +1 -0
  8. package/images/icons/admin.svg +1 -0
  9. package/images/icons/archive.svg +1 -0
  10. package/images/icons/arrow_down.svg +1 -0
  11. package/images/icons/arrow_left.svg +1 -0
  12. package/images/icons/arrow_right.svg +1 -0
  13. package/images/icons/arrow_up.svg +1 -0
  14. package/images/icons/assign.svg +1 -0
  15. package/images/icons/attachment.svg +1 -0
  16. package/images/icons/back.svg +1 -0
  17. package/images/icons/badge.svg +1 -0
  18. package/images/icons/board.svg +1 -0
  19. package/images/icons/branch.svg +1 -0
  20. package/images/icons/bug.svg +1 -0
  21. package/images/icons/calculator.svg +1 -0
  22. package/images/icons/checkmark.svg +1 -0
  23. package/images/icons/close.svg +1 -0
  24. package/images/icons/clypboard.svg +1 -0
  25. package/images/icons/comment.svg +1 -0
  26. package/images/icons/complete.svg +1 -0
  27. package/images/icons/dashboard.svg +1 -0
  28. package/images/icons/data_pull.svg +1 -0
  29. package/images/icons/data_update.svg +1 -0
  30. package/images/icons/database.svg +1 -0
  31. package/images/icons/day.svg +1 -0
  32. package/images/icons/delete.svg +1 -0
  33. package/images/icons/documentation.svg +1 -0
  34. package/images/icons/edit.svg +1 -0
  35. package/images/icons/feature.svg +1 -0
  36. package/images/icons/flex.svg +1 -0
  37. package/images/icons/forward.svg +1 -0
  38. package/images/icons/github.svg +1 -0
  39. package/images/icons/history.svg +1 -0
  40. package/images/icons/home.svg +1 -0
  41. package/images/icons/image.svg +1 -0
  42. package/images/icons/inbox.svg +1 -0
  43. package/images/icons/info.svg +1 -0
  44. package/images/icons/issue.svg +1 -0
  45. package/images/icons/lane.svg +1 -0
  46. package/images/icons/lane_asap.svg +1 -0
  47. package/images/icons/lane_days.svg +1 -0
  48. package/images/icons/lane_hours.svg +1 -0
  49. package/images/icons/lane_weeks.svg +1 -0
  50. package/images/icons/lanes_board.svg +1 -0
  51. package/images/icons/level_complete.svg +1 -0
  52. package/images/icons/level_highway.svg +1 -0
  53. package/images/icons/level_on_ramp.svg +1 -0
  54. package/images/icons/level_parking.svg +1 -0
  55. package/images/icons/minus.svg +1 -0
  56. package/images/icons/night.svg +1 -0
  57. package/images/icons/origin.svg +1 -0
  58. package/images/icons/pending.svg +1 -0
  59. package/images/icons/plus.svg +1 -0
  60. package/images/icons/post.svg +1 -0
  61. package/images/icons/pr_closed.svg +1 -0
  62. package/images/icons/pr_merged.svg +1 -0
  63. package/images/icons/pr_open.svg +1 -0
  64. package/images/icons/prioritized.svg +1 -0
  65. package/images/icons/project.svg +1 -0
  66. package/images/icons/question.svg +1 -0
  67. package/images/icons/reaction.svg +1 -0
  68. package/images/icons/recent.svg +1 -0
  69. package/images/icons/refresh.svg +1 -0
  70. package/images/icons/request.svg +1 -0
  71. package/images/icons/settings.svg +1 -0
  72. package/images/icons/status.svg +1 -0
  73. package/images/icons/step_deploy.svg +1 -0
  74. package/images/icons/step_develop.svg +1 -0
  75. package/images/icons/step_investigate.svg +1 -0
  76. package/images/icons/step_review.svg +1 -0
  77. package/images/icons/step_test.svg +1 -0
  78. package/images/icons/steps.svg +1 -0
  79. package/images/icons/steps_board.svg +1 -0
  80. package/images/icons/subscribe.svg +1 -0
  81. package/images/icons/support.svg +1 -0
  82. package/images/icons/terrier.svg +1 -0
  83. package/images/icons/thumbs_up.svg +1 -0
  84. package/images/icons/type.svg +1 -0
  85. package/images/icons/unprioritized.svg +1 -0
  86. package/images/icons/upload.svg +1 -0
  87. package/images/icons/user.svg +1 -0
  88. package/images/icons/users.svg +1 -0
  89. package/images/optimized/icon-active.svg +1 -0
  90. package/images/optimized/icon-admin.svg +1 -0
  91. package/images/optimized/icon-archive.svg +1 -0
  92. package/images/optimized/icon-arrow_down.svg +1 -0
  93. package/images/optimized/icon-arrow_left.svg +1 -0
  94. package/images/optimized/icon-arrow_right.svg +1 -0
  95. package/images/optimized/icon-arrow_up.svg +1 -0
  96. package/images/optimized/icon-assign.svg +1 -0
  97. package/images/optimized/icon-attachment.svg +1 -0
  98. package/images/optimized/icon-back.svg +1 -0
  99. package/images/optimized/icon-badge.svg +1 -0
  100. package/images/optimized/icon-board.svg +1 -0
  101. package/images/optimized/icon-branch.svg +1 -0
  102. package/images/optimized/icon-bug.svg +1 -0
  103. package/images/optimized/icon-calculator.svg +1 -0
  104. package/images/optimized/icon-checkmark.svg +1 -0
  105. package/images/optimized/icon-close.svg +1 -0
  106. package/images/optimized/icon-clypboard.svg +1 -0
  107. package/images/optimized/icon-comment.svg +1 -0
  108. package/images/optimized/icon-complete.svg +1 -0
  109. package/images/optimized/icon-dashboard.svg +1 -0
  110. package/images/optimized/icon-data_pull.svg +1 -0
  111. package/images/optimized/icon-data_update.svg +1 -0
  112. package/images/optimized/icon-database.svg +1 -0
  113. package/images/optimized/icon-day.svg +1 -0
  114. package/images/optimized/icon-delete.svg +1 -0
  115. package/images/optimized/icon-documentation.svg +1 -0
  116. package/images/optimized/icon-edit.svg +1 -0
  117. package/images/optimized/icon-feature.svg +1 -0
  118. package/images/optimized/icon-flex.svg +1 -0
  119. package/images/optimized/icon-forward.svg +1 -0
  120. package/images/optimized/icon-github.svg +1 -0
  121. package/images/optimized/icon-history.svg +1 -0
  122. package/images/optimized/icon-home.svg +1 -0
  123. package/images/optimized/icon-image.svg +1 -0
  124. package/images/optimized/icon-inbox.svg +1 -0
  125. package/images/optimized/icon-info.svg +1 -0
  126. package/images/optimized/icon-issue.svg +1 -0
  127. package/images/optimized/icon-lane.svg +1 -0
  128. package/images/optimized/icon-lane_asap.svg +1 -0
  129. package/images/optimized/icon-lane_days.svg +1 -0
  130. package/images/optimized/icon-lane_hours.svg +1 -0
  131. package/images/optimized/icon-lane_weeks.svg +1 -0
  132. package/images/optimized/icon-lanes_board.svg +1 -0
  133. package/images/optimized/icon-level_complete.svg +1 -0
  134. package/images/optimized/icon-level_highway.svg +1 -0
  135. package/images/optimized/icon-level_on_ramp.svg +1 -0
  136. package/images/optimized/icon-level_parking.svg +1 -0
  137. package/images/optimized/icon-minus.svg +1 -0
  138. package/images/optimized/icon-night.svg +1 -0
  139. package/images/optimized/icon-origin.svg +1 -0
  140. package/images/optimized/icon-pending.svg +1 -0
  141. package/images/optimized/icon-plus.svg +1 -0
  142. package/images/optimized/icon-post.svg +1 -0
  143. package/images/optimized/icon-pr_closed.svg +1 -0
  144. package/images/optimized/icon-pr_merged.svg +1 -0
  145. package/images/optimized/icon-pr_open.svg +1 -0
  146. package/images/optimized/icon-prioritized.svg +1 -0
  147. package/images/optimized/icon-project.svg +1 -0
  148. package/images/optimized/icon-question.svg +1 -0
  149. package/images/optimized/icon-reaction.svg +1 -0
  150. package/images/optimized/icon-recent.svg +1 -0
  151. package/images/optimized/icon-refresh.svg +1 -0
  152. package/images/optimized/icon-request.svg +1 -0
  153. package/images/optimized/icon-settings.svg +1 -0
  154. package/images/optimized/icon-status.svg +1 -0
  155. package/images/optimized/icon-step_deploy.svg +1 -0
  156. package/images/optimized/icon-step_develop.svg +1 -0
  157. package/images/optimized/icon-step_investigate.svg +1 -0
  158. package/images/optimized/icon-step_review.svg +1 -0
  159. package/images/optimized/icon-step_test.svg +1 -0
  160. package/images/optimized/icon-steps.svg +1 -0
  161. package/images/optimized/icon-steps_board.svg +1 -0
  162. package/images/optimized/icon-subscribe.svg +1 -0
  163. package/images/optimized/icon-support.svg +1 -0
  164. package/images/optimized/icon-terrier.svg +1 -0
  165. package/images/optimized/icon-thumbs_up.svg +1 -0
  166. package/images/optimized/icon-type.svg +1 -0
  167. package/images/optimized/icon-unprioritized.svg +1 -0
  168. package/images/optimized/icon-upload.svg +1 -0
  169. package/images/optimized/icon-user.svg +1 -0
  170. package/images/optimized/icon-users.svg +1 -0
  171. package/images/optimized/terrier-hub-favicon.svg +1 -0
  172. package/images/optimized/terrier-hub-icon-dark.svg +1 -0
  173. package/images/optimized/terrier-hub-icon-light.svg +1 -0
  174. package/images/optimized/terrier-hub-loader.svg +1 -0
  175. package/images/optimized/terrier-hub-logo-dark.svg +1 -0
  176. package/images/optimized/terrier-hub-logo-light.svg +1 -0
  177. package/images/raw/icon-active.svg +8 -0
  178. package/images/raw/icon-admin.svg +9 -0
  179. package/images/raw/icon-archive.svg +9 -0
  180. package/images/raw/icon-arrow_down.svg +7 -0
  181. package/images/raw/icon-arrow_left.svg +7 -0
  182. package/images/raw/icon-arrow_right.svg +7 -0
  183. package/images/raw/icon-arrow_up.svg +7 -0
  184. package/images/raw/icon-assign.svg +8 -0
  185. package/images/raw/icon-attachment.svg +7 -0
  186. package/images/raw/icon-back.svg +7 -0
  187. package/images/raw/icon-badge.svg +10 -0
  188. package/images/raw/icon-board.svg +20 -0
  189. package/images/raw/icon-branch.svg +11 -0
  190. package/images/raw/icon-bug.svg +8 -0
  191. package/images/raw/icon-calculator.svg +31 -0
  192. package/images/raw/icon-checkmark.svg +8 -0
  193. package/images/raw/icon-close.svg +8 -0
  194. package/images/raw/icon-clypboard.svg +9 -0
  195. package/images/raw/icon-comment.svg +12 -0
  196. package/images/raw/icon-complete.svg +8 -0
  197. package/images/raw/icon-dashboard.svg +18 -0
  198. package/images/raw/icon-data_pull.svg +9 -0
  199. package/images/raw/icon-data_update.svg +9 -0
  200. package/images/raw/icon-database.svg +10 -0
  201. package/images/raw/icon-day.svg +19 -0
  202. package/images/raw/icon-delete.svg +11 -0
  203. package/images/raw/icon-documentation.svg +21 -0
  204. package/images/raw/icon-edit.svg +11 -0
  205. package/images/raw/icon-feature.svg +7 -0
  206. package/images/raw/icon-flex.svg +6 -0
  207. package/images/raw/icon-forward.svg +7 -0
  208. package/images/raw/icon-github.svg +8 -0
  209. package/images/raw/icon-history.svg +12 -0
  210. package/images/raw/icon-home.svg +8 -0
  211. package/images/raw/icon-image.svg +9 -0
  212. package/images/raw/icon-inbox.svg +9 -0
  213. package/images/raw/icon-info.svg +11 -0
  214. package/images/raw/icon-issue.svg +10 -0
  215. package/images/raw/icon-lane.svg +9 -0
  216. package/images/raw/icon-lane_asap.svg +9 -0
  217. package/images/raw/icon-lane_days.svg +11 -0
  218. package/images/raw/icon-lane_hours.svg +8 -0
  219. package/images/raw/icon-lane_weeks.svg +10 -0
  220. package/images/raw/icon-lanes_board.svg +10 -0
  221. package/images/raw/icon-level_complete.svg +8 -0
  222. package/images/raw/icon-level_highway.svg +11 -0
  223. package/images/raw/icon-level_on_ramp.svg +8 -0
  224. package/images/raw/icon-level_parking.svg +10 -0
  225. package/images/raw/icon-minus.svg +8 -0
  226. package/images/raw/icon-night.svg +9 -0
  227. package/images/raw/icon-origin.svg +11 -0
  228. package/images/raw/icon-pending.svg +10 -0
  229. package/images/raw/icon-plus.svg +8 -0
  230. package/images/raw/icon-post.svg +12 -0
  231. package/images/raw/icon-pr_closed.svg +15 -0
  232. package/images/raw/icon-pr_merged.svg +11 -0
  233. package/images/raw/icon-pr_open.svg +12 -0
  234. package/images/raw/icon-prioritized.svg +11 -0
  235. package/images/raw/icon-project.svg +24 -0
  236. package/images/raw/icon-question.svg +9 -0
  237. package/images/raw/icon-reaction.svg +6 -0
  238. package/images/raw/icon-recent.svg +12 -0
  239. package/images/raw/icon-refresh.svg +11 -0
  240. package/images/raw/icon-request.svg +10 -0
  241. package/images/raw/icon-settings.svg +11 -0
  242. package/images/raw/icon-status.svg +8 -0
  243. package/images/raw/icon-step_deploy.svg +15 -0
  244. package/images/raw/icon-step_develop.svg +12 -0
  245. package/images/raw/icon-step_investigate.svg +14 -0
  246. package/images/raw/icon-step_review.svg +11 -0
  247. package/images/raw/icon-step_test.svg +14 -0
  248. package/images/raw/icon-steps.svg +18 -0
  249. package/images/raw/icon-steps_board.svg +19 -0
  250. package/images/raw/icon-subscribe.svg +10 -0
  251. package/images/raw/icon-support.svg +14 -0
  252. package/images/raw/icon-terrier.svg +7 -0
  253. package/images/raw/icon-thumbs_up.svg +1 -0
  254. package/images/raw/icon-type.svg +15 -0
  255. package/images/raw/icon-unprioritized.svg +10 -0
  256. package/images/raw/icon-upload.svg +8 -0
  257. package/images/raw/icon-user.svg +9 -0
  258. package/images/raw/icon-users.svg +14 -0
  259. package/images/raw/terrier-hub-favicon-alert.png +0 -0
  260. package/images/raw/terrier-hub-favicon-dark.png +0 -0
  261. package/images/raw/terrier-hub-favicon.png +0 -0
  262. package/images/raw/terrier-hub-favicon.svg +29 -0
  263. package/images/raw/terrier-hub-icon-dark.svg +23 -0
  264. package/images/raw/terrier-hub-icon-light.png +0 -0
  265. package/images/raw/terrier-hub-icon-light.svg +23 -0
  266. package/images/raw/terrier-hub-loader.svg +54 -0
  267. package/images/raw/terrier-hub-logo-dark.svg +27 -0
  268. package/images/raw/terrier-hub-logo-light.png +0 -0
  269. package/images/raw/terrier-hub-logo-light.svg +28 -0
  270. package/lightbox.ts +9 -22
  271. package/loading.ts +5 -6
  272. package/modals.ts +8 -19
  273. package/overlays.ts +100 -33
  274. package/package.json +1 -1
  275. package/parts/content-part.ts +187 -0
  276. package/parts/not-found-page.ts +20 -0
  277. package/parts/page-part.ts +189 -0
  278. package/parts/panel-part.ts +40 -0
  279. package/parts/terrier-form-part.ts +20 -0
  280. package/parts/terrier-part.ts +89 -0
  281. package/schema.ts +28 -1
  282. package/tabs.ts +164 -0
  283. package/theme.ts +41 -12
  284. package/toasts.ts +10 -10
  285. package/tooltips.ts +2 -2
  286. 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<TT extends ThemeType> = {
32
+ export type Action = {
22
33
  title?: string
23
34
  tooltip?: string
24
- icon?: TT['icons']
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<TT extends ThemeType> = {
35
- iconColor?: TT['colors'] | null
36
- badgeColor?: TT['colors']
45
+ export type RenderActionOptions = {
46
+ iconColor?: ColorName | null
47
+ badgeColor?: ColorName
37
48
  defaultClass?: string
38
49
  }
39
50
 
40
- export default abstract class Theme<TT extends ThemeType> {
41
- abstract renderIcon(parent: PartTag, icon: TT['icons'], color?: TT['colors'] | null): void
42
-
43
- abstract renderCloseIcon(parent: PartTag, color?: TT['colors'] | null): void
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
- abstract colorValue(name: TT['colors']): string
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
- abstract getLoaderSrc(): string
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<TT> | Action<TT>[], options?: RenderActionOptions<TT>) {
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 { Logger } from "tuff-core/logging"
2
- import { createElement } from "tuff-core/html"
3
- import Theme, {ThemeType} from "./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<TT extends ThemeType> = {
8
- color: TT['colors']
9
- icon?: TT['icons']
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<TT extends ThemeType>(message: string, options: ToastOptions<TT>, theme: Theme<TT>) {
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 { createElement } from "tuff-core/html"
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)