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
package/parts.ts DELETED
@@ -1,485 +0,0 @@
1
- import { Logger } from "tuff-core/logging"
2
- import { Part } from "tuff-core/parts"
3
- import {PartParent, PartTag, NoState} from "tuff-core/parts"
4
- import Fragments from "./fragments"
5
- import {Dropdown} from "./dropdowns"
6
- import {TerrierApp} from "./app"
7
- import Loading from "./loading"
8
- import Theme, {Action, RenderActionOptions, ThemeType} from "./theme"
9
- import Toasts, {ToastOptions} from "./toasts";
10
- import {FormPart, FormPartData} from "tuff-core/forms"
11
-
12
- const log = new Logger('Parts')
13
-
14
- export type PanelActions<TT extends ThemeType> = {
15
- primary: Array<Action<TT>>
16
- secondary: Array<Action<TT>>
17
- tertiary: Array<Action<TT>>
18
- }
19
-
20
- export type ActionLevel = keyof PanelActions<any>
21
-
22
-
23
- ////////////////////////////////////////////////////////////////////////////////
24
- // Terrier Part
25
- ////////////////////////////////////////////////////////////////////////////////
26
-
27
- /**
28
- * Base class for ALL parts in a Terrier application.
29
- */
30
- export abstract class TerrierPart<
31
- TState,
32
- TThemeType extends ThemeType,
33
- TApp extends TerrierApp<TThemeType, TApp, TTheme>,
34
- TTheme extends Theme<TThemeType>
35
- > extends Part<TState> {
36
-
37
- get app(): TApp {
38
- return this.root as TApp // this should always be true
39
- }
40
-
41
- get theme(): TTheme {
42
- return this.app.theme
43
- }
44
-
45
- /// Loading
46
-
47
- /**
48
- * This can be overloaded if the loading overlay should go
49
- * somewhere other than the part's root element.
50
- */
51
- getLoadingContainer(): Element | null | undefined {
52
- return this.element
53
- }
54
-
55
-
56
- /**
57
- * Shows the loading animation on top of the part.
58
- */
59
- startLoading() {
60
- const elem = this.getLoadingContainer()
61
- if (!elem) {
62
- return
63
- }
64
- Loading.showOverlay(elem, this.theme)
65
- }
66
-
67
- /**
68
- * Removes the loading animation from the part.
69
- */
70
- stopLoading() {
71
- const elem = this.getLoadingContainer()
72
- if (!elem) {
73
- return
74
- }
75
- Loading.removeOverlay(elem)
76
- }
77
-
78
- /**
79
- * Shows the loading overlay until the given function completes (either returns successfully or throws an exception)
80
- * @param func
81
- */
82
- showLoading(func: () => void): void
83
- showLoading(func: () => Promise<void>): Promise<void>
84
- showLoading(func: () => void | Promise<void>): void | Promise<void> {
85
- this.startLoading()
86
- let stopImmediately = true
87
- try {
88
- const res = func()
89
- if (res) {
90
- stopImmediately = false
91
- return res.finally(() => {
92
- this.stopLoading()
93
- })
94
- }
95
- } finally {
96
- if (stopImmediately) {
97
- this.stopLoading()
98
- }
99
- }
100
- }
101
-
102
-
103
- /// Toasts
104
-
105
- /**
106
- * Shows a toast message in a bubble in the upper right corner.
107
- * @param message the message text
108
- * @param options
109
- */
110
- showToast(message: string, options: ToastOptions<TThemeType>) {
111
- Toasts.show(message, options, this.theme)
112
- }
113
-
114
- }
115
-
116
-
117
- ////////////////////////////////////////////////////////////////////////////////
118
- // Content Part
119
- ////////////////////////////////////////////////////////////////////////////////
120
-
121
- /**
122
- * Base class for all Parts that render some main content, like pages, panels, and modals.
123
- */
124
- export abstract class ContentPart<
125
- TState,
126
- TThemeType extends ThemeType,
127
- TApp extends TerrierApp<TThemeType, TApp, TTheme>,
128
- TTheme extends Theme<TThemeType>
129
- > extends TerrierPart<TState, TThemeType, TApp, TTheme> {
130
-
131
- /**
132
- * All ContentParts must implement this to render their actual content.
133
- * @param parent
134
- */
135
- abstract renderContent(parent: PartTag): void
136
-
137
-
138
- protected _title = ''
139
-
140
- /**
141
- * Sets the page, panel, or modal title.
142
- * @param title
143
- */
144
- setTitle(title: string) {
145
- this._title = title
146
- }
147
-
148
- protected _icon: TThemeType['icons'] | null = null
149
-
150
- setIcon(icon: TThemeType['icons']) {
151
- this._icon = icon
152
- }
153
-
154
- protected _breadcrumbClasses: string[] = []
155
-
156
- addBreadcrumbClass(c: string) {
157
- this._breadcrumbClasses.push(c)
158
- }
159
-
160
-
161
- /// Actions
162
-
163
- // stored actions can be either an action object or a reference to a named action
164
- actions = {
165
- primary: Array<Action<TThemeType> | string>(),
166
- secondary: Array<Action<TThemeType> | string>(),
167
- tertiary: Array<Action<TThemeType> | string>()
168
- }
169
-
170
- namedActions: Record<string, { action: Action<TThemeType>, level: ActionLevel }> = {}
171
-
172
- /**
173
- * Add an action to the part, or replace a named action if it already exists.
174
- * @param action the action to add
175
- * @param level whether it's a primary, secondary, or tertiary action
176
- * @param name a name to be given to this action, so it can be accessed later
177
- */
178
- addAction(action: Action<TThemeType>, level: ActionLevel = 'primary', name?: string) {
179
- if (name?.length) {
180
- if (name in this.namedActions) {
181
- const currentLevel = this.namedActions[name].level
182
- if (level != currentLevel) {
183
- const index = this.actions[currentLevel].indexOf(name)
184
- this.actions[currentLevel].splice(index, 1)
185
- this.actions[level].push(name)
186
- }
187
- this.namedActions[name].action = action
188
- } else {
189
- this.namedActions[name] = { action, level }
190
- this.actions[level].push(name)
191
- }
192
- } else {
193
- this.actions[level].push(action)
194
- }
195
- }
196
-
197
- /**
198
- * Returns the action definition for the action with the given name, or undefined if there is no action with that name
199
- * @param name
200
- */
201
- getNamedAction(name: string): Action<TThemeType> | undefined {
202
- return this.namedActions[name].action
203
- }
204
-
205
- /**
206
- * Removes the action with the given name
207
- * @param name
208
- */
209
- removeNamedAction(name: string) {
210
- if (!(name in this.namedActions)) return
211
- const level = this.actions[this.namedActions[name].level]
212
- delete this.namedActions[name]
213
- const actionIndex = level.indexOf(name)
214
- if (actionIndex >= 0) {
215
- level.splice(actionIndex, 1)
216
- }
217
- }
218
-
219
- /**
220
- * Clears the actions for this part
221
- * @param level whether to clear the primary, secondary, or both sets of actions
222
- */
223
- clearActions(level: ActionLevel) {
224
- for (const action of this.actions[level]) {
225
- if (typeof action === 'string') {
226
- delete this.namedActions[action]
227
- }
228
- }
229
- this.actions[level] = []
230
- }
231
-
232
- getAllActions(): PanelActions<TThemeType> {
233
- return {
234
- primary: this.getActions('primary'),
235
- secondary: this.getActions('secondary'),
236
- tertiary: this.getActions('tertiary'),
237
- }
238
- }
239
-
240
- getActions(level: ActionLevel): Action<TThemeType>[] {
241
- return this.actions[level].map(action => {
242
- return (typeof action === 'string') ? this.namedActions[action].action : action
243
- })
244
- }
245
-
246
-
247
- /// Dropdowns
248
-
249
- /**
250
- * Shows the given dropdown part on the page.
251
- * It's generally better to call `toggleDropdown` instead so that the dropdown will be
252
- * hidden upon a subsequent click on the target.
253
- * @param constructor a constructor for a dropdown part
254
- * @param state the dropdown's state
255
- * @param target the target element around which to show the dropdown
256
- */
257
- makeDropdown<DropdownType extends Dropdown<DropdownStateType, TThemeType, TApp, TTheme>, DropdownStateType>(
258
- constructor: {new(p: PartParent, id: string, state: DropdownStateType): DropdownType;},
259
- state: DropdownStateType,
260
- target: EventTarget | null) {
261
- if (!(target && target instanceof HTMLElement)) {
262
- throw "Trying to show a dropdown without an element target!"
263
- }
264
- const dropdown = this.app.makeOverlay(constructor, state, 'dropdown')
265
- dropdown.parentPart = this
266
- dropdown.anchor(target)
267
- this.app.lastDropdownTarget = target
268
- }
269
-
270
- clearDropdown() {
271
- this.app.clearOverlay('dropdown')
272
- }
273
-
274
- /**
275
- * Calls `makeDropdown` only if there's not a dropdown currently originating from the target.
276
- * @param constructor a constructor for a dropdown part
277
- * @param state the dropdown's state
278
- * @param target the target element around which to show the dropdown
279
- */
280
- toggleDropdown<DropdownType extends Dropdown<DropdownStateType, TThemeType, TApp, TTheme>, DropdownStateType>(
281
- constructor: { new(p: PartParent, id: string, state: DropdownStateType): DropdownType; },
282
- state: DropdownStateType,
283
- target: EventTarget | null) {
284
- if (target && target instanceof HTMLElement && target == this.app.lastDropdownTarget) {
285
- this.clearDropdown()
286
- } else {
287
- this.makeDropdown(constructor, state, target)
288
- }
289
- }
290
-
291
- }
292
-
293
-
294
- ////////////////////////////////////////////////////////////////////////////////
295
- // Page
296
- ////////////////////////////////////////////////////////////////////////////////
297
-
298
- /**
299
- * Whether some content should be constrained to a reasonable width or span the entire screen.
300
- */
301
- export type ContentWidth = "normal" | "wide"
302
-
303
-
304
- /**
305
- * A part that renders content to a full page.
306
- */
307
- export abstract class PagePart<
308
- TState,
309
- TThemeType extends ThemeType,
310
- TApp extends TerrierApp<TThemeType, TApp, TTheme>,
311
- TTheme extends Theme<TThemeType>
312
- > extends ContentPart<TState, TThemeType, TApp, TTheme> {
313
-
314
- /// Breadcrumbs
315
-
316
- private _breadcrumbs = Array<Action<TThemeType>>()
317
-
318
- addBreadcrumb(crumb: Action<TThemeType>) {
319
- this._breadcrumbs.push(crumb)
320
- }
321
-
322
-
323
- /**
324
- * Sets both the page title and the last breadcrumb.
325
- * @param title
326
- */
327
- setTitle(title: string) {
328
- super.setTitle(title)
329
- document.title = `${title} :: Terrier Hub`
330
- }
331
-
332
- private _titleHref?: string
333
-
334
- /**
335
- * Adds an href to the title (last) breadcrumb.
336
- * @param href
337
- */
338
- setTitleHref(href: string) {
339
- this._titleHref = href
340
- }
341
-
342
- /**
343
- * Whether the main content should be constrained to a reasonable width (default) or span the entire screen.
344
- */
345
- protected mainContentWidth: ContentWidth = "normal"
346
-
347
- render(parent: PartTag) {
348
- parent.div(`.tt-page-part.content-width-${this.mainContentWidth}`, page => {
349
- page.div('.tt-flex.top-row', topRow => {
350
- this.renderBreadcrumbs(topRow);
351
-
352
- if (this.actions.tertiary.length) {
353
- this.renderActions(topRow, 'tertiary');
354
- }
355
- })
356
-
357
- page.div('.lighting')
358
- page.div('.page-main', main => {
359
- this.renderContent(main)
360
- main.div('.page-actions', actions => {
361
- this.renderActions(actions, 'secondary', {iconColor: null, defaultClass: 'secondary'})
362
- this.renderActions(actions, 'primary', {iconColor: null, defaultClass: 'primary'})
363
- })
364
- })
365
- })
366
- }
367
-
368
- protected renderActions(parent: PartTag, level: ActionLevel, options?: RenderActionOptions<TThemeType>) {
369
- parent.div(`.${level}-actions`, actions => {
370
- this.app.theme.renderActions(actions, this.getActions(level), options)
371
- })
372
- }
373
-
374
- protected renderBreadcrumbs(parent: PartTag) {
375
- if (!this._breadcrumbs.length && !this._title?.length) return
376
-
377
- parent.h1('.breadcrumbs', h1 => {
378
- const crumbs = Array.from(this._breadcrumbs)
379
-
380
- // add a breadcrumb for the page title
381
- const titleCrumb: Action<TThemeType> = {
382
- title: this._title,
383
- icon: this._icon || undefined,
384
- }
385
- if (this._titleHref) {
386
- titleCrumb.href = this._titleHref
387
- }
388
- if (this._breadcrumbClasses?.length) {
389
- titleCrumb.classes = this._breadcrumbClasses
390
- }
391
- crumbs.push(titleCrumb)
392
-
393
- this.app.theme.renderActions(h1, crumbs)
394
- })
395
- }
396
- }
397
-
398
-
399
-
400
- /**
401
- * Default page part if the router can't find the path.
402
- */
403
- export class NotFoundRoute<
404
- TT extends ThemeType,
405
- TApp extends TerrierApp<TT, TApp, TTheme>,
406
- TTheme extends Theme<TT>
407
- > extends PagePart<NoState, TT, TApp, TTheme> {
408
- async init() {
409
- this.setTitle("Page Not Found")
410
- }
411
-
412
- renderContent(parent: PartTag) {
413
- log.warn(`Not found: ${this.context.href}`)
414
- parent.h1({text: "Not Found"})
415
- }
416
-
417
- }
418
-
419
-
420
- ////////////////////////////////////////////////////////////////////////////////
421
- // Panel
422
- ////////////////////////////////////////////////////////////////////////////////
423
-
424
- /**
425
- * A part that renders content inside a panel.
426
- */
427
- export abstract class PanelPart<
428
- TState,
429
- TThemeType extends ThemeType,
430
- TApp extends TerrierApp<TThemeType, TApp, TTheme>,
431
- TTheme extends Theme<TThemeType>
432
- > extends ContentPart<TState, TThemeType, TApp, TTheme> {
433
-
434
- getLoadingContainer() {
435
- return this.element?.getElementsByClassName('tt-panel')[0]
436
- }
437
-
438
- protected get panelClasses(): string[] {
439
- return []
440
- }
441
-
442
- render(parent: PartTag) {
443
- parent.div('.tt-panel', panel => {
444
- panel.class(...this.panelClasses)
445
- if (this._title?.length || this.actions.tertiary.length) {
446
- panel.div('.panel-header', header => {
447
- header.h2(h2 => {
448
- if (this._icon) {
449
- this.app.theme.renderIcon(h2, this._icon, 'link')
450
- }
451
- h2.div('.title', {text: this._title || 'Call setTitle()'})
452
- })
453
- this.theme.renderActions(header, this.getActions('tertiary'))
454
- })
455
- }
456
- panel.div('.panel-content', content => {
457
- this.renderContent(content)
458
- })
459
- Fragments.panelActions(panel, this.getAllActions(), this.theme)
460
- })
461
- }
462
- }
463
-
464
-
465
- ////////////////////////////////////////////////////////////////////////////////
466
- // Form Part
467
- ////////////////////////////////////////////////////////////////////////////////
468
-
469
- export abstract class ThemedFormPart<
470
- TState extends FormPartData,
471
- TThemeType extends ThemeType,
472
- TApp extends TerrierApp<TThemeType, TApp, TTheme>,
473
- TTheme extends Theme<TThemeType>
474
- > extends FormPart<TState> {
475
-
476
-
477
- get app(): TApp {
478
- return this.root as TApp // this should always be true
479
- }
480
-
481
- get theme(): TTheme {
482
- return this.app.theme
483
- }
484
-
485
- }