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/app.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import { Logger } from "tuff-core/logging"
2
- import {Part, PartParent} from "tuff-core/parts"
3
- import {TerrierPart} from "./parts"
2
+ import {Part, PartConstructor, PartParent} from "tuff-core/parts"
3
+ import TerrierPart from "./parts/terrier-part"
4
4
  import Tooltips from "./tooltips"
5
5
  import Lightbox from "./lightbox"
6
+ import Theme from "./theme"
7
+ import {ModalPart, ModalStackPart} from "./modals"
8
+ import {OverlayLayerType, OverlayPart} from "./overlays"
6
9
 
7
10
  // @ts-ignore
8
11
  import logoUrl from './images/optimized/terrier-hub-logo-light.svg'
9
- import Theme, {ThemeType} from "./theme"
10
- import {ModalPart, ModalStackPart} from "./modals"
11
- import {OverlayLayer, OverlayPart} from "./overlays"
12
12
 
13
13
  const log = new Logger('App')
14
14
  Logger.level = 'info'
@@ -16,22 +16,18 @@ Logger.level = 'info'
16
16
  /**
17
17
  * Main application part that renders the entire page.
18
18
  */
19
- export abstract class TerrierApp<
20
- TThemeType extends ThemeType,
21
- TSelf extends TerrierApp<TThemeType, TSelf, TTheme>,
22
- TTheme extends Theme<TThemeType>
23
- > extends TerrierPart<{theme: TTheme}, TThemeType, TSelf, TTheme> {
24
-
25
- _theme!: TTheme
19
+ export abstract class TerrierApp<TState> extends TerrierPart<TState> {
26
20
 
27
- get theme(): TTheme {
21
+ _theme!: Theme
22
+
23
+ get theme(): Theme {
28
24
  return this._theme
29
25
  }
30
26
 
31
27
  overlayPart!: OverlayPart
32
28
 
33
29
  async init() {
34
- this._theme = this.state.theme
30
+ this._theme = new Theme()
35
31
  this.overlayPart = this.makePart(OverlayPart, {})
36
32
  log.info("Initialized")
37
33
  }
@@ -44,42 +40,56 @@ export abstract class TerrierApp<
44
40
  update(root: HTMLElement) {
45
41
  log.info(`Update`, root)
46
42
  Tooltips.init(root)
47
- Lightbox.init<TThemeType, TSelf, TTheme>(root, this as unknown as TSelf, 'body-content')
43
+ Lightbox.init(root, this, 'body-content')
48
44
  }
49
45
 
50
46
 
51
47
  /// Overlays
52
48
 
53
- makeOverlay<OverlayType extends Part<StateType>, StateType>(
49
+ addOverlay<OverlayType extends Part<StateType>, StateType extends {}>(
54
50
  constructor: { new(p: PartParent, id: string, state: StateType): OverlayType; },
55
51
  state: StateType,
56
- layer: OverlayLayer
57
- ): OverlayType {
58
- return this.overlayPart.makeLayer(constructor, state, layer)
52
+ type: OverlayLayerType
53
+ ) {
54
+ return this.overlayPart.pushLayer(constructor, state, type)
59
55
  }
60
56
 
61
- clearOverlay(layer: OverlayLayer) {
62
- this.overlayPart.clearLayer(layer)
63
- this.lastDropdownTarget = undefined
57
+ removeOverlay<StateType extends {}>(state: StateType): boolean {
58
+ return this.overlayPart.removeLayer(state)
59
+ }
60
+
61
+ popOverlay(type?: OverlayLayerType) {
62
+ this.overlayPart.popLayer(type)
64
63
  }
65
64
 
66
65
  clearOverlays() {
67
66
  this.overlayPart.clearAll()
68
67
  }
69
68
 
69
+ removeDropdown<StateType>(state: StateType): boolean {
70
+ this.lastDropdownTarget = undefined
71
+ return this.overlayPart.removeLayer(state)
72
+ }
73
+
74
+ clearDropdowns() {
75
+ this.lastDropdownTarget = undefined
76
+ this.popOverlay('dropdown')
77
+ }
78
+
70
79
 
71
80
  lastDropdownTarget?: HTMLElement
72
81
 
73
82
 
74
83
  /// Modals
75
84
 
76
- showModal<ModalType extends ModalPart<StateType, TThemeType, TSelf, TTheme>, StateType>(constructor: { new(p: PartParent, id: string, state: StateType): ModalType; }, state: StateType): ModalType {
77
- const modalStack =
78
- (this.overlayPart.parts.modal as ModalStackPart<TThemeType, TSelf, TTheme, ModalType>)
79
- ?? this.makeOverlay(ModalStackPart, {}, 'modal')
85
+ showModal<ModalType extends ModalPart<StateType>, StateType>(
86
+ constructor: PartConstructor<ModalType, StateType>,
87
+ state: StateType
88
+ ): ModalType {
89
+ const modalStack = this.overlayPart.getOrCreateLayer(ModalStackPart, {}, 'modal')
80
90
  const modal = modalStack.pushModal(constructor, state)
81
91
  modalStack.dirty()
82
- return modal
92
+ return modal as ModalType
83
93
  }
84
94
 
85
95
 
package/dropdowns.ts CHANGED
@@ -1,12 +1,11 @@
1
- import { Logger } from "tuff-core/logging"
2
- import { untypedKey } from "tuff-core/messages"
3
- import { unique } from "tuff-core/arrays"
1
+ import {Logger} from "tuff-core/logging"
2
+ import {untypedKey} from "tuff-core/messages"
3
+ import {unique} from "tuff-core/arrays"
4
4
  import {PartTag, StatelessPart} from "tuff-core/parts"
5
5
  import Overlays from "./overlays"
6
- import {TerrierPart} from "./parts"
6
+ import TerrierPart from "./parts/terrier-part"
7
7
  import Objects from "tuff-core/objects"
8
- import Theme, {Action, ThemeType} from "./theme"
9
- import {TerrierApp} from "./app";
8
+ import {Action} from "./theme"
10
9
 
11
10
  const log = new Logger('Dropdowns')
12
11
 
@@ -16,12 +15,7 @@ const clearDropdownKey = untypedKey()
16
15
  * Abstract base class for dropdown parts.
17
16
  * Subclasses must implement the `renderContent()` method to render the dropdown content.
18
17
  */
19
- export abstract class Dropdown<
20
- TState,
21
- TThemeType extends ThemeType,
22
- TApp extends TerrierApp<TThemeType, TApp, TTheme>,
23
- TTheme extends Theme<TThemeType>
24
- > extends TerrierPart<TState, TThemeType, TApp, TTheme> {
18
+ export abstract class Dropdown<TState> extends TerrierPart<TState> {
25
19
 
26
20
  parentPart?: StatelessPart
27
21
 
@@ -29,12 +23,20 @@ export abstract class Dropdown<
29
23
  return ['tt-dropdown', ...super.parentClasses]
30
24
  }
31
25
 
26
+ /**
27
+ * Override and return true to have the dropdown close when the user clicks anywhere outside of it.
28
+ */
29
+ get autoClose(): boolean {
30
+ return false
31
+ }
32
+
32
33
  // the computed absolute position of the
33
34
  left = 0
34
35
  top = 0
35
36
 
36
37
  async init() {
37
- this.onClick(clearDropdownKey, _ => {
38
+ this.onClick(clearDropdownKey, m => {
39
+ log.info("Clearing dropdown", m)
38
40
  this.clear()
39
41
  })
40
42
  }
@@ -44,11 +46,15 @@ export abstract class Dropdown<
44
46
  */
45
47
  clear() {
46
48
  log.info("Clearing dropdown")
47
- this.app.clearOverlay('dropdown')
49
+ this.app.removeDropdown(this.state)
48
50
  }
49
51
 
50
52
  render(parent: PartTag) {
51
- parent.div('.dropdown-content', content => {
53
+ if (this.autoClose) {
54
+ parent.div('.tt-dropdown-backdrop')
55
+ .emitClick(clearDropdownKey)
56
+ }
57
+ parent.div('.tt-dropdown-content', content => {
52
58
  this.renderContent(content)
53
59
  })
54
60
  }
@@ -71,7 +77,7 @@ export abstract class Dropdown<
71
77
  }
72
78
 
73
79
  update(_elem: HTMLElement) {
74
- const content = _elem.querySelector('.dropdown-content')
80
+ const content = _elem.querySelector('.tt-dropdown-content')
75
81
  if (this.anchorTarget && content) {
76
82
  log.info(`Anchoring dropdown`, content, this.anchorTarget)
77
83
  Overlays.anchorElement(content as HTMLElement, this.anchorTarget)
@@ -84,12 +90,11 @@ export abstract class Dropdown<
84
90
  /**
85
91
  * A concrete dropdown part that shows a list of actions.
86
92
  */
87
- export class ActionsDropdown<
88
- TThemeType extends ThemeType,
89
- TApp extends TerrierApp<TThemeType, TApp, TTheme>,
90
- TTheme extends Theme<TThemeType>
91
- > extends Dropdown<Array<Action<TThemeType>>, TThemeType, TApp, TTheme> {
93
+ export class ActionsDropdown extends Dropdown<Array<Action>> {
92
94
 
95
+ get autoClose(): boolean {
96
+ return true
97
+ }
93
98
 
94
99
  get parentClasses(): Array<string> {
95
100
  return ['tt-actions-dropdown', ...super.parentClasses]
@@ -101,7 +106,7 @@ export class ActionsDropdown<
101
106
  const keys = unique(this.state.map(action => action.click?.key).filter(Objects.notNull))
102
107
  for (const key of keys) {
103
108
  this.onClick(key, m => {
104
- this.app.clearOverlay('dropdown')
109
+ this.clear()
105
110
  log.info(`Re-emitting ${key.id} message`, m, this.parentPart)
106
111
  if (this.parentPart) {
107
112
  this.parentPart.emit('click', key, m.event, m.data)
package/forms.ts ADDED
@@ -0,0 +1,33 @@
1
+ import {SelectOptions} from "tuff-core/forms"
2
+ import {strings} from "tuff-core"
3
+
4
+ ////////////////////////////////////////////////////////////////////////////////
5
+ // Options
6
+ ////////////////////////////////////////////////////////////////////////////////
7
+
8
+ /**
9
+ * Computes a `SelectOptions` array by titleizing the values in a plain string array.
10
+ * @param opts
11
+ */
12
+ function titleizeOptions(opts: string[], blank?: string): SelectOptions {
13
+ const out = opts.map(c => {
14
+ return {value: c, title: strings.titleize(c)}
15
+ })
16
+ if (blank != undefined) { // don't test length, allow it to be a blank string
17
+ out.unshift({title: blank, value: ''})
18
+ }
19
+ return out
20
+ }
21
+
22
+
23
+ ////////////////////////////////////////////////////////////////////////////////
24
+ // Export
25
+ ////////////////////////////////////////////////////////////////////////////////
26
+
27
+ const Forms = {
28
+ titleizeOptions
29
+ }
30
+
31
+ export default Forms
32
+
33
+
package/fragments.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import {PartTag} from "tuff-core/parts"
2
2
  import {AnchorTagAttrs, HtmlParentTag} from "tuff-core/html"
3
- import Theme, {Action, Packet, ThemeType} from "./theme"
4
- import {ActionLevel, PanelActions} from "./parts"
3
+ import Theme, {Action, ColorName, IconName, Packet} from "./theme"
4
+ import {ActionLevel, PanelActions} from "./parts/content-part"
5
5
 
6
6
  /**
7
7
  * Base class for Panel and Card fragment builders.
8
8
  */
9
- abstract class ContentFragment<TT extends ThemeType> {
10
- protected constructor(readonly prefix: string, readonly theme: Theme<TT>) {
11
-
9
+ abstract class ContentFragment {
10
+ protected constructor(readonly prefix: string, readonly theme: Theme) {
11
+
12
12
  }
13
13
 
14
14
  protected _title?: string
@@ -17,18 +17,18 @@ abstract class ContentFragment<TT extends ThemeType> {
17
17
  * @param t the title
18
18
  * @param icon the optional icon
19
19
  */
20
- title(t: string, icon?: TT['icons']) {
20
+ title(t: string, icon?: IconName) {
21
21
  this._title = t
22
22
  this._icon = icon
23
23
  return this
24
24
  }
25
25
 
26
- protected _icon?: TT['icons']
26
+ protected _icon?: IconName
27
27
 
28
28
  /**
29
29
  * @param i the panel icon
30
30
  */
31
- icon(i: TT['icons']) {
31
+ icon(i: IconName) {
32
32
  this._icon = i
33
33
  return this
34
34
  }
@@ -46,17 +46,17 @@ abstract class ContentFragment<TT extends ThemeType> {
46
46
  }
47
47
 
48
48
 
49
- export class PanelFragment<TT extends ThemeType> extends ContentFragment<TT> {
50
- constructor(theme: Theme<TT>) {
49
+ export class PanelFragment extends ContentFragment {
50
+ constructor(theme: Theme) {
51
51
  super('tt-panel', theme)
52
52
  }
53
53
 
54
54
  /// Actions
55
55
 
56
56
  actions = {
57
- primary: Array<Action<TT>>(),
58
- secondary: Array<Action<TT>>(),
59
- tertiary: Array<Action<TT>>()
57
+ primary: Array<Action>(),
58
+ secondary: Array<Action>(),
59
+ tertiary: Array<Action>()
60
60
  }
61
61
 
62
62
  /**
@@ -64,7 +64,7 @@ export class PanelFragment<TT extends ThemeType> extends ContentFragment<TT> {
64
64
  * @param action the action to add
65
65
  * @param level whether it's a primary, secondary, or tertiary action
66
66
  */
67
- addAction(action: Action<TT>, level: ActionLevel = 'primary') {
67
+ addAction(action: Action, level: ActionLevel = 'primary') {
68
68
  this.actions[level].push(action)
69
69
  return this
70
70
  }
@@ -102,16 +102,18 @@ export class PanelFragment<TT extends ThemeType> extends ContentFragment<TT> {
102
102
  * Render the primary and secondary actions to the bottom of a panel
103
103
  * @param panel the .panel container
104
104
  * @param actions the actions
105
+ * @param theme the theme with which to render actions
105
106
  */
106
- function panelActions<TT extends ThemeType>(panel: PartTag, actions: PanelActions<TT>, theme: Theme<TT>) {
107
+ function panelActions(panel: PartTag, actions: PanelActions, theme: Theme) {
107
108
  if (actions.primary.length || actions.secondary.length) {
108
109
  panel.div('.panel-actions', actionsContainer => {
109
- actionsContainer.div('.secondary-actions', secondaryContainer => {
110
- theme.renderActions(secondaryContainer, actions.secondary, {iconColor: 'white', defaultClass: 'link'})
111
- })
112
- actionsContainer.div('.primary-actions', primaryContainer => {
113
- theme.renderActions(primaryContainer, actions.primary, {iconColor: 'white'})
114
- })
110
+ for (const level of ['secondary', 'primary'] as const) {
111
+ const levelActions = actions[level]
112
+ if (!levelActions?.length) continue;
113
+ actionsContainer.div(`.${level}-actions`, container => {
114
+ theme.renderActions(container, levelActions, { iconColor: 'white', defaultClass: level })
115
+ })
116
+ }
115
117
  })
116
118
  }
117
119
  }
@@ -120,8 +122,8 @@ function panelActions<TT extends ThemeType>(panel: PartTag, actions: PanelAction
120
122
  /**
121
123
  * Cards are like panels except they don't have any actions and are themselves an anchor.
122
124
  */
123
- class CardFragment<TT extends ThemeType> extends ContentFragment<TT> {
124
- constructor(theme: Theme<TT>) {
125
+ class CardFragment extends ContentFragment {
126
+ constructor(theme: Theme) {
125
127
  super('tt-card', theme)
126
128
  }
127
129
 
@@ -162,15 +164,15 @@ class CardFragment<TT extends ThemeType> extends ContentFragment<TT> {
162
164
  }
163
165
 
164
166
 
165
- class LabeledValueFragment<TT extends ThemeType> extends ContentFragment<TT> {
167
+ class LabeledValueFragment extends ContentFragment {
166
168
 
167
- constructor(theme: Theme<TT>) {
169
+ constructor(theme: Theme) {
168
170
  super('tt-labeled-value', theme)
169
171
  }
170
172
 
171
173
  private _value?: string
172
- private _valueIcon?: TT['icons']
173
- private _valueIconColor?: TT['colors'] | null
174
+ private _valueIcon?: IconName
175
+ private _valueIconColor?: ColorName | null
174
176
  private _valueClass?: string[]
175
177
 
176
178
  private _href?: string
@@ -180,7 +182,7 @@ class LabeledValueFragment<TT extends ThemeType> extends ContentFragment<TT> {
180
182
 
181
183
  private _tooltip?: string
182
184
 
183
- value(value: string, icon?: TT['icons'], iconColor?: TT['colors'] | null) {
185
+ value(value: string, icon?: IconName, iconColor?: ColorName | null) {
184
186
  this._value = value
185
187
  this._valueIcon = icon
186
188
  this._valueIconColor = iconColor
@@ -256,10 +258,10 @@ type ListValueDefinition = {
256
258
  tooltip?: string
257
259
  }
258
260
 
259
- class LabeledListFragment<TT extends ThemeType> extends ContentFragment<TT> {
261
+ class LabeledListFragment extends ContentFragment {
260
262
  private _values?: ListValueDefinition[]
261
263
 
262
- constructor(theme: Theme<TT>) {
264
+ constructor(theme: Theme) {
263
265
  super('tt-labeled-list', theme)
264
266
  }
265
267
 
@@ -305,7 +307,7 @@ class LabeledListFragment<TT extends ThemeType> extends ContentFragment<TT> {
305
307
  * Creates a new card fragment builder.
306
308
  * Make sure to call `render()` in order to render it to a parent tag.
307
309
  */
308
- function card<TT extends ThemeType>(theme: Theme<TT>) {
310
+ function card(theme: Theme) {
309
311
  return new CardFragment(theme)
310
312
  }
311
313
 
@@ -314,7 +316,7 @@ function card<TT extends ThemeType>(theme: Theme<TT>) {
314
316
  * Creates a new panel fragment builder.
315
317
  * Make sure to call `render()` in order to render it to a parent tag.
316
318
  */
317
- function panel<TT extends ThemeType>(theme: Theme<TT>) {
319
+ function panel(theme: Theme) {
318
320
  return new PanelFragment(theme)
319
321
  }
320
322
 
@@ -322,18 +324,18 @@ function panel<TT extends ThemeType>(theme: Theme<TT>) {
322
324
  * Creates a new labeled value fragment builder.
323
325
  * Make sure to call `render()` in order to render it to a parent tag.
324
326
  */
325
- function labeledValue<TT extends ThemeType>(theme: Theme<TT>) {
327
+ function labeledValue(theme: Theme) {
326
328
  return new LabeledValueFragment(theme)
327
329
  }
328
330
 
329
- function labeledList<TT extends ThemeType>(theme: Theme<TT>) {
331
+ function labeledList(theme: Theme) {
330
332
  return new LabeledListFragment(theme)
331
333
  }
332
334
 
333
335
  /**
334
336
  * Create a new button in the parent.
335
337
  */
336
- function button<TT extends ThemeType>(parent: PartTag, theme: Theme<TT>, title?: string, icon?: TT['icons'], iconColor: TT['colors'] | null = null) {
338
+ function button(parent: PartTag, theme: Theme, title?: string, icon?: IconName, iconColor: ColorName | null = null) {
337
339
  return parent.a('.tt-button', button => {
338
340
  if (icon) theme.renderIcon(button, icon, iconColor)
339
341
  if (title?.length) button.div('.title', {text: title})
@@ -344,7 +346,7 @@ function button<TT extends ThemeType>(parent: PartTag, theme: Theme<TT>, title?:
344
346
  * Create a new simple value display in the parent.
345
347
  * This is just some text with an optional icon that doesn't have a separate label.
346
348
  */
347
- function simpleValue<TT extends ThemeType>(parent: PartTag, theme: Theme<TT>, title: string, icon?: TT['icons'], iconColor: TT['colors'] | null = 'link') {
349
+ function simpleValue(parent: PartTag, theme: Theme, title: string, icon?: IconName, iconColor: ColorName | null = 'link') {
348
350
  return parent.div('.tt-simple-value.shrink', button => {
349
351
  if (icon) theme.renderIcon(button, icon, iconColor)
350
352
  button.div('.title', {text: title})
@@ -354,7 +356,7 @@ function simpleValue<TT extends ThemeType>(parent: PartTag, theme: Theme<TT>, ti
354
356
  /**
355
357
  * Helper to create a heading with an optional icon.
356
358
  */
357
- function simpleHeading<TT extends ThemeType>(parent: PartTag, theme: Theme<TT>, title: string, icon?: TT['icons']) {
359
+ function simpleHeading(parent: PartTag, theme: Theme, title: string, icon?: IconName) {
358
360
  return parent.h3('.shrink', heading => {
359
361
  if (icon) {
360
362
  theme.renderIcon(heading, icon, 'link')