@leavittsoftware/web 9.9.0 → 10.0.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 (223) hide show
  1. package/.ncurc.js +1 -1
  2. package/CLAUDE.md +1320 -0
  3. package/LICENSE.md +1 -1
  4. package/leavitt/api-service/api-service.js +3 -2
  5. package/leavitt/api-service/api-service.js.map +1 -1
  6. package/leavitt/api-service/blob-response.js +1 -0
  7. package/leavitt/api-service/blob-response.js.map +1 -1
  8. package/leavitt/app/app-logo.js +1 -1
  9. package/leavitt/app/app-logo.js.map +1 -1
  10. package/leavitt/app/app-main-content-container.js +5 -0
  11. package/leavitt/app/app-main-content-container.js.map +1 -1
  12. package/leavitt/app/app-navigation-header.js +10 -10
  13. package/leavitt/app/app-navigation-header.js.map +1 -1
  14. package/leavitt/company-select/company-select.js +2 -2
  15. package/leavitt/company-select/company-select.js.map +1 -1
  16. package/leavitt/email-history-viewer/email-history-viewer-filled-filter-dialog.d.ts +3 -369
  17. package/leavitt/email-history-viewer/email-history-viewer-filled-filter-dialog.js +15 -9
  18. package/leavitt/email-history-viewer/email-history-viewer-filled-filter-dialog.js.map +1 -1
  19. package/leavitt/email-history-viewer/email-history-viewer-filled.d.ts +3 -374
  20. package/leavitt/email-history-viewer/email-history-viewer-filled.js +15 -33
  21. package/leavitt/email-history-viewer/email-history-viewer-filled.js.map +1 -1
  22. package/leavitt/email-history-viewer/view-email-template-info-dialog.d.ts +3 -369
  23. package/leavitt/email-history-viewer/view-email-template-info-dialog.js +12 -5
  24. package/leavitt/email-history-viewer/view-email-template-info-dialog.js.map +1 -1
  25. package/leavitt/email-history-viewer/view-sent-email-dialog.d.ts +3 -369
  26. package/leavitt/email-history-viewer/view-sent-email-dialog.js +13 -6
  27. package/leavitt/email-history-viewer/view-sent-email-dialog.js.map +1 -1
  28. package/leavitt/error-page/error-page.d.ts +4 -2
  29. package/leavitt/error-page/error-page.js +126 -37
  30. package/leavitt/error-page/error-page.js.map +1 -1
  31. package/leavitt/file-explorer/add-folder-modal.d.ts +3 -369
  32. package/leavitt/file-explorer/add-folder-modal.js +13 -6
  33. package/leavitt/file-explorer/add-folder-modal.js.map +1 -1
  34. package/leavitt/file-explorer/file-explorer-image.js.map +1 -1
  35. package/leavitt/file-explorer/file-explorer.d.ts +5 -371
  36. package/leavitt/file-explorer/file-explorer.js +35 -30
  37. package/leavitt/file-explorer/file-explorer.js.map +1 -1
  38. package/leavitt/file-explorer/file-list-item.js.map +1 -1
  39. package/leavitt/file-explorer/file-modal.d.ts +3 -369
  40. package/leavitt/file-explorer/file-modal.js +12 -5
  41. package/leavitt/file-explorer/file-modal.js.map +1 -1
  42. package/leavitt/file-explorer/folder-list-item.js.map +1 -1
  43. package/leavitt/file-explorer/folder-modal.d.ts +3 -369
  44. package/leavitt/file-explorer/folder-modal.js +12 -5
  45. package/leavitt/file-explorer/folder-modal.js.map +1 -1
  46. package/leavitt/person-company-select/person-company-select.js +5 -3
  47. package/leavitt/person-company-select/person-company-select.js.map +1 -1
  48. package/leavitt/person-group-select/person-group-select.js +5 -3
  49. package/leavitt/person-group-select/person-group-select.js.map +1 -1
  50. package/leavitt/person-select/person-select.js +3 -2
  51. package/leavitt/person-select/person-select.js.map +1 -1
  52. package/leavitt/profile-picture/profile-picture-menu.js +2 -0
  53. package/leavitt/profile-picture/profile-picture-menu.js.map +1 -1
  54. package/leavitt/profile-picture/profile-picture.js +4 -4
  55. package/leavitt/profile-picture/profile-picture.js.map +1 -1
  56. package/leavitt/service-worker-notifier/service-worker-notifier.js.map +1 -1
  57. package/leavitt/theme/theme-preference.js +4 -0
  58. package/leavitt/theme/theme-preference.js.map +1 -1
  59. package/leavitt/user-feedback/provide-feedback-dialog.d.ts +3 -369
  60. package/leavitt/user-feedback/provide-feedback-dialog.js +11 -4
  61. package/leavitt/user-feedback/provide-feedback-dialog.js.map +1 -1
  62. package/leavitt/user-feedback/report-a-problem-dialog.d.ts +3 -369
  63. package/leavitt/user-feedback/report-a-problem-dialog.js +11 -4
  64. package/leavitt/user-feedback/report-a-problem-dialog.js.map +1 -1
  65. package/leavitt/user-manager/auth-zero-lg-user-manager.js +5 -5
  66. package/leavitt/user-manager/auth-zero-lg-user-manager.js.map +1 -1
  67. package/package.json +8 -10
  68. package/titanium/address-input/address-input.js +0 -2
  69. package/titanium/address-input/address-input.js.map +1 -1
  70. package/titanium/address-input/google-address-input.d.ts +1 -0
  71. package/titanium/address-input/google-address-input.js +62 -45
  72. package/titanium/address-input/google-address-input.js.map +1 -1
  73. package/titanium/address-input/google-maps-process-shim.d.ts +2 -0
  74. package/titanium/address-input/google-maps-process-shim.js +12 -0
  75. package/titanium/address-input/google-maps-process-shim.js.map +1 -0
  76. package/titanium/address-input/manual-address-dialog.d.ts +2 -7
  77. package/titanium/address-input/manual-address-dialog.js +21 -34
  78. package/titanium/address-input/manual-address-dialog.js.map +1 -1
  79. package/titanium/address-input/utils/place-result-to-address.d.ts +1 -1
  80. package/titanium/address-input/utils/place-result-to-address.js +31 -30
  81. package/titanium/address-input/utils/place-result-to-address.js.map +1 -1
  82. package/titanium/chip/chip.d.ts +0 -1
  83. package/titanium/chip/chip.js +7 -17
  84. package/titanium/chip/chip.js.map +1 -1
  85. package/titanium/chip-multi-select/chip-multi-select.d.ts +3 -8
  86. package/titanium/chip-multi-select/chip-multi-select.js +41 -62
  87. package/titanium/chip-multi-select/chip-multi-select.js.map +1 -1
  88. package/titanium/circle-loading-indicator/circle-loading-indicator.js +5 -5
  89. package/titanium/circle-loading-indicator/circle-loading-indicator.js.map +1 -1
  90. package/titanium/confirmation-dialog/confirmation-dialog.js +2 -0
  91. package/titanium/confirmation-dialog/confirmation-dialog.js.map +1 -1
  92. package/titanium/data-table/data-table-action-bar.js +1 -5
  93. package/titanium/data-table/data-table-action-bar.js.map +1 -1
  94. package/titanium/data-table/data-table-core-reorder-dialog.d.ts +3 -369
  95. package/titanium/data-table/data-table-core-reorder-dialog.js +10 -3
  96. package/titanium/data-table/data-table-core-reorder-dialog.js.map +1 -1
  97. package/titanium/data-table/data-table-core-reorder-item.js.map +1 -1
  98. package/titanium/data-table/data-table-core-settings-choose-columns-dialog.js.map +1 -1
  99. package/titanium/data-table/data-table-core-settings-choose-columns-item.js +3 -3
  100. package/titanium/data-table/data-table-core-settings-choose-columns-item.js.map +1 -1
  101. package/titanium/data-table/data-table-core-settings-sort-dialog.js.map +1 -1
  102. package/titanium/data-table/data-table-core-settings-sort-item.js +3 -3
  103. package/titanium/data-table/data-table-core-settings-sort-item.js.map +1 -1
  104. package/titanium/data-table/data-table-core.d.ts +5 -369
  105. package/titanium/data-table/data-table-core.js +13 -2
  106. package/titanium/data-table/data-table-core.js.map +1 -1
  107. package/titanium/data-table/draggable-item-base.js +6 -0
  108. package/titanium/data-table/draggable-item-base.js.map +1 -1
  109. package/titanium/data-table/filter-controller.js.map +1 -1
  110. package/titanium/data-table/page-control.d.ts +3 -8
  111. package/titanium/data-table/page-control.js +13 -33
  112. package/titanium/data-table/page-control.js.map +1 -1
  113. package/titanium/date-input/date-input.d.ts +1 -6
  114. package/titanium/date-input/date-input.js +8 -44
  115. package/titanium/date-input/date-input.js.map +1 -1
  116. package/titanium/date-range-selector/date-range-selector.d.ts +1 -6
  117. package/titanium/date-range-selector/date-range-selector.js +15 -35
  118. package/titanium/date-range-selector/date-range-selector.js.map +1 -1
  119. package/titanium/drawer/drawer.d.ts +1 -6
  120. package/titanium/drawer/drawer.js +4 -23
  121. package/titanium/drawer/drawer.js.map +1 -1
  122. package/titanium/event-bus/event-bus.js.map +1 -1
  123. package/titanium/helpers/debouncer.js.map +1 -1
  124. package/titanium/helpers/helpers.d.ts +0 -1
  125. package/titanium/helpers/helpers.js +0 -1
  126. package/titanium/helpers/helpers.js.map +1 -1
  127. package/titanium/helpers/pending-state-catcher.d.ts +7 -406
  128. package/titanium/helpers/pending-state-catcher.js +85 -82
  129. package/titanium/helpers/pending-state-catcher.js.map +1 -1
  130. package/titanium/input-validator/filled-input-validator.js.map +1 -1
  131. package/titanium/profile-picture-stack/profile-picture-stack.js +2 -2
  132. package/titanium/profile-picture-stack/profile-picture-stack.js.map +1 -1
  133. package/titanium/search-input/filled-search-input.js.map +1 -1
  134. package/titanium/show-hide/show-hide.d.ts +1 -6
  135. package/titanium/show-hide/show-hide.js +2 -18
  136. package/titanium/show-hide/show-hide.js.map +1 -1
  137. package/titanium/single-select-base/single-select-base.d.ts +11 -381
  138. package/titanium/single-select-base/single-select-base.js +43 -50
  139. package/titanium/single-select-base/single-select-base.js.map +1 -1
  140. package/titanium/smart-attachment-input/crop-and-save-image-dialog.d.ts +3 -369
  141. package/titanium/smart-attachment-input/crop-and-save-image-dialog.js +21 -16
  142. package/titanium/smart-attachment-input/crop-and-save-image-dialog.js.map +1 -1
  143. package/titanium/smart-attachment-input/image-preview-dialog.js.map +1 -1
  144. package/titanium/smart-attachment-input/smart-attachment-input.d.ts +1 -6
  145. package/titanium/smart-attachment-input/smart-attachment-input.js +5 -18
  146. package/titanium/smart-attachment-input/smart-attachment-input.js.map +1 -1
  147. package/titanium/snackbar/snackbar-stack.js +2 -2
  148. package/titanium/snackbar/snackbar-stack.js.map +1 -1
  149. package/titanium/snackbar/snackbars/http-error-snackbar.js +1 -1
  150. package/titanium/snackbar/snackbars/http-error-snackbar.js.map +1 -1
  151. package/titanium/snackbar/snackbars/simple-snackbar.js +3 -3
  152. package/titanium/snackbar/snackbars/simple-snackbar.js.map +1 -1
  153. package/titanium/toolbar/toolbar.js +1 -1
  154. package/titanium/toolbar/toolbar.js.map +1 -1
  155. package/leavitt/api-service/object-to-form-data.test.d.ts +0 -2
  156. package/leavitt/api-service/object-to-form-data.test.js +0 -229
  157. package/leavitt/api-service/object-to-form-data.test.js.map +0 -1
  158. package/leavitt/email-history-viewer/email-history-view-list-filter-dialog.d.ts +0 -394
  159. package/leavitt/email-history-viewer/email-history-view-list-filter-dialog.js +0 -233
  160. package/leavitt/email-history-viewer/email-history-view-list-filter-dialog.js.map +0 -1
  161. package/leavitt/email-history-viewer/email-history-viewer.d.ts +0 -413
  162. package/leavitt/email-history-viewer/email-history-viewer.js +0 -403
  163. package/leavitt/email-history-viewer/email-history-viewer.js.map +0 -1
  164. package/leavitt/user-feedback/user-feedback.d.ts +0 -390
  165. package/leavitt/user-feedback/user-feedback.js +0 -221
  166. package/leavitt/user-feedback/user-feedback.js.map +0 -1
  167. package/titanium/access-denied-page/access-denied-page.d.ts +0 -16
  168. package/titanium/access-denied-page/access-denied-page.js +0 -359
  169. package/titanium/access-denied-page/access-denied-page.js.map +0 -1
  170. package/titanium/card/card.d.ts +0 -24
  171. package/titanium/card/card.js +0 -188
  172. package/titanium/card/card.js.map +0 -1
  173. package/titanium/confirm-dialog/confirm-dialog-open-event.d.ts +0 -10
  174. package/titanium/confirm-dialog/confirm-dialog-open-event.js +0 -12
  175. package/titanium/confirm-dialog/confirm-dialog-open-event.js.map +0 -1
  176. package/titanium/confirm-dialog/confirm-dialog.d.ts +0 -25
  177. package/titanium/confirm-dialog/confirm-dialog.js +0 -104
  178. package/titanium/confirm-dialog/confirm-dialog.js.map +0 -1
  179. package/titanium/data-table/data-table-header.d.ts +0 -68
  180. package/titanium/data-table/data-table-header.js +0 -295
  181. package/titanium/data-table/data-table-header.js.map +0 -1
  182. package/titanium/data-table/data-table-item.d.ts +0 -99
  183. package/titanium/data-table/data-table-item.js +0 -561
  184. package/titanium/data-table/data-table-item.js.map +0 -1
  185. package/titanium/data-table/data-table.d.ts +0 -127
  186. package/titanium/data-table/data-table.js +0 -688
  187. package/titanium/data-table/data-table.js.map +0 -1
  188. package/titanium/duration-input/duration-input.d.ts +0 -31
  189. package/titanium/duration-input/duration-input.js +0 -126
  190. package/titanium/duration-input/duration-input.js.map +0 -1
  191. package/titanium/duration-input/outlined-duration-input.d.ts +0 -30
  192. package/titanium/duration-input/outlined-duration-input.js +0 -109
  193. package/titanium/duration-input/outlined-duration-input.js.map +0 -1
  194. package/titanium/error-page/error-page.d.ts +0 -16
  195. package/titanium/error-page/error-page.js +0 -95
  196. package/titanium/error-page/error-page.js.map +0 -1
  197. package/titanium/extendable-outlined-text-field/extendable-outlined-text-field.d.ts +0 -168
  198. package/titanium/extendable-outlined-text-field/extendable-outlined-text-field.js +0 -373
  199. package/titanium/extendable-outlined-text-field/extendable-outlined-text-field.js.map +0 -1
  200. package/titanium/full-page-loading-indicator/full-page-loading-indicator.d.ts +0 -17
  201. package/titanium/full-page-loading-indicator/full-page-loading-indicator.js +0 -121
  202. package/titanium/full-page-loading-indicator/full-page-loading-indicator.js.map +0 -1
  203. package/titanium/header/header.d.ts +0 -39
  204. package/titanium/header/header.js +0 -167
  205. package/titanium/header/header.js.map +0 -1
  206. package/titanium/helpers/load-while.d.ts +0 -374
  207. package/titanium/helpers/load-while.js +0 -28
  208. package/titanium/helpers/load-while.js.map +0 -1
  209. package/titanium/input-validator/input-validator.d.ts +0 -20
  210. package/titanium/input-validator/input-validator.js +0 -48
  211. package/titanium/input-validator/input-validator.js.map +0 -1
  212. package/titanium/input-validator/outlined-input-validator.d.ts +0 -20
  213. package/titanium/input-validator/outlined-input-validator.js +0 -48
  214. package/titanium/input-validator/outlined-input-validator.js.map +0 -1
  215. package/titanium/search-input/search-input.d.ts +0 -29
  216. package/titanium/search-input/search-input.js +0 -123
  217. package/titanium/search-input/search-input.js.map +0 -1
  218. package/titanium/service-worker-notifier/service-worker-notifier.d.ts +0 -9
  219. package/titanium/service-worker-notifier/service-worker-notifier.js +0 -58
  220. package/titanium/service-worker-notifier/service-worker-notifier.js.map +0 -1
  221. package/titanium/youtube-input/youtube-input.d.ts +0 -20
  222. package/titanium/youtube-input/youtube-input.js +0 -72
  223. package/titanium/youtube-input/youtube-input.js.map +0 -1
package/CLAUDE.md ADDED
@@ -0,0 +1,1320 @@
1
+ # @leavittsoftware/web
2
+
3
+ Component and utility reference for agents consuming this package. Read this before diving into source.
4
+
5
+ ## Package overview
6
+
7
+ `@leavittsoftware/web` is a collection of Lit 3 web components built on [Material Web](https://github.com/material-components/material-web).
8
+
9
+ - **`titanium-*`** — general-purpose UI (drawers, tables, inputs, snackbars, loading indicators)
10
+ - **`leavitt-*`** — Leavitt Group domain components (selects, file explorer, app shell, email viewer); many require `ApiService` and authentication
11
+
12
+ **Browser support:** Chrome, Safari, Firefox, Edge
13
+
14
+ **Install:**
15
+
16
+ ```bash
17
+ npm i @leavittsoftware/web
18
+ ```
19
+
20
+ ## Upgrade changelog (for agents)
21
+
22
+ When bumping `@leavittsoftware/web` in a downstream project, read every entry **after** the version currently installed. Each entry lists grep targets, removals, and replacements — not full release history (that lives in-repo at `CHANGELOG.md`).
23
+
24
+ ### Unreleased
25
+
26
+ **Upgrade if coming from:** `< unreleased` (published latest is `9.9.0`)
27
+
28
+ **Search downstream for:**
29
+
30
+ - `LoadWhile` — mixin import or `extend LoadWhile(` on **page** components only (not `dataTable.loadWhile`)
31
+ - `titanium-data-table` tag or import (legacy stack — not `data-table-core`)
32
+ - `titanium-confirm-dialog` (not `confirmation-dialog`)
33
+ - `titanium-error-page`, `titanium-access-denied-page`, `titanium-full-page-loading-indicator`
34
+ - `titanium-card`, `titanium-header`
35
+ - `leavitt-user-feedback` (shell component)
36
+ - `leavitt/email-history-viewer/email-history-viewer` (unfilled legacy viewer)
37
+ - `outlined-duration-input`, `outlined-input-validator`, unqualified `search-input` / `youtube-input` / `duration-input` import paths
38
+ - `filled` attribute on date-input, date-range-selector, chip, chip-multi-select, page-control, show-hide, smart-attachment-input, single-select-base subclasses, manual-address-dialog
39
+ - `always-show-content` on `titanium-drawer`
40
+ - `toolbarSearchTerm`, `toolbar-search-term` on `leavitt-email-history-viewer-filled`
41
+
42
+ **Removed** — delete imports/usages:
43
+
44
+ | Removed | Replacement |
45
+ | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
46
+ | `@leavittsoftware/web/titanium/helpers/load-while` | `@promiseTracking` on page components — see Loading — `promiseTracking` |
47
+ | `titanium-confirm-dialog` | `titanium-confirmation-dialog` |
48
+ | `titanium-error-page` | `leavitt-error-page` |
49
+ | Legacy `titanium-data-table`, `-item`, `-header` | `titanium-data-table-core` + action bar + page control |
50
+ | `leavitt-user-feedback` | `provide-feedback-dialog` / `report-a-problem-dialog` directly |
51
+ | `leavitt/email-history-viewer/email-history-viewer` | `leavitt-email-history-viewer-filled` only |
52
+ | `titanium-card`, `titanium-header`, `titanium-access-denied-page`, `titanium-full-page-loading-indicator` | App-specific UI (no direct replacement) |
53
+ | Outlined / unqualified input variants | Filled paths only (e.g. `filled-search-input`, `filled-duration-input`) |
54
+ | `always-show-content` on `titanium-drawer` | Set `mode="inline"` / `mode="flyover"` and call `open()` for inline sidebars |
55
+ | `toolbarSearchTerm` / `toolbar-search-term` on `leavitt-email-history-viewer-filled` | `.siteSearchTextFieldContext` + `TitaniumSiteSearchTextFieldController` — see Site search |
56
+
57
+ **Renamed / API changes:**
58
+
59
+ - Page-level `LoadWhile` mixin / `this.loadWhile()` on the page host → `@promiseTracking` + `trackLoadingPromise` (see Loading — `promiseTracking`)
60
+ - `dataTable.loadWhile(promise)` — **still supported** via deprecated alias on `titanium-data-table-core`; prefer `trackLoadingPromise` in new code
61
+ - `itemMetaData[].sortExpression` → `getSortExpression: () => string` (since 9.0; still required if not yet migrated)
62
+
63
+ **Behavior / styling:**
64
+
65
+ - `filled` attribute removed from dual-style components — filled Material styling is always on
66
+ - `google-address-input` uses `@googlemaps/js-api-loader` v2 internally; consumers still only pass `googleMapsApiKey` — no bundler `process` shim required. Enable **Places API (New)** (`places.googleapis.com`) plus **Maps JavaScript API** on the key's GCP project (legacy Places API is not enough).
67
+ - `leavitt-error-page` no longer uses tsParticles; no consumer particle config to migrate
68
+
69
+ ### 9.4.0
70
+
71
+ **Upgrade if coming from:** `< 9.4.0`
72
+
73
+ **Adopt before unreleased / 10.x:**
74
+
75
+ - `@promiseTracking` decorator added — preferred for page-level loading flags
76
+ - If page components still `extend LoadWhile(LitElement)`, migrate to `@promiseTracking` + `trackLoadingPromise` before upgrading past 9.9.x
77
+ - `dataTable.loadWhile(promise)` call sites do **not** need changing (compat alias on `titanium-data-table-core`)
78
+
79
+ ### 9.0.0
80
+
81
+ **Upgrade if coming from:** `< 9.0.0`
82
+
83
+ **Search downstream for:**
84
+
85
+ - `UserManager`, `GetUserManagerInstance`, `AuthenticatedTokenProvider`, `UserManagerUpdatedEvent`
86
+ - `sortExpression:` in `TitaniumDataTableCoreMetaData` column defs
87
+
88
+ **Renamed / API changes:**
89
+
90
+ - Auth helpers above → `AuthZeroLgUserManager` (see Services — `AuthZeroLgUserManager`)
91
+ - `itemMetaData[].sortExpression: 'Name'` → `getSortExpression: () => 'Name'`
92
+
93
+ ## Import and registration
94
+
95
+ There is no barrel `index.ts` and no `exports` map. Always use deep file paths:
96
+
97
+ ```ts
98
+ // Register element (side-effect — required before using the tag in HTML)
99
+ import '@leavittsoftware/web/titanium/drawer/drawer.js';
100
+
101
+ // Import class, types, or styles
102
+ import { TitaniumDrawer } from '@leavittsoftware/web/titanium/drawer/drawer.js';
103
+ import { h2, p } from '@leavittsoftware/web/titanium/styles/styles.js';
104
+ ```
105
+
106
+ - Published builds use `.js` extensions in import paths
107
+ - Material Web elements (`md-filled-button`, `md-dialog`, etc.) must be imported separately by the consumer
108
+ - Paths mirror source: `titanium/<feature>/<file>` or `leavitt/<feature>/<file>`
109
+
110
+ ## Material Web foundation
111
+
112
+ Most `titanium-*` and `leavitt-*` elements are **built on** [Material Web](https://github.com/material-components/material-web) (`@material/web/*`) — either by **extending** an `Md*` class or by **composing** `md-*` tags inside a `LitElement` wrapper. When debugging API, validation, slots, or styling, check the matching Material Web component docs in addition to this file.
113
+
114
+ ### Extend vs compose
115
+
116
+ | Pattern | What it means | Where to look for extra API |
117
+ | ------- | ------------- | --------------------------- |
118
+ | **Extends** | `class TitaniumFoo extends MdFilledTextField` (or `MdFilledField`, etc.) | Material Web docs for that base class — properties, attributes, methods, slots, and CSS parts apply on the **titanium tag** |
119
+ | **Composes** | `class TitaniumFoo extends LitElement` and renders `<md-filled-text-field>` (or `md-dialog`, `md-menu`, …) in `render()` | This file + Material Web docs for the **inner** `md-*` tag; the titanium tag exposes its own curated API |
120
+
121
+ Titanium **mixins and decorators** (`ThemePreference`, `promiseTracking`, …) apply to the **host class** they decorate. They do not replace Material Web behavior — an input that extends `MdFilledTextField` still has all text-field properties; one that composes `md-filled-text-field` exposes titanium properties on the host and Material Web behavior on the child.
122
+
123
+ ### Published components by pattern
124
+
125
+ | Tag / family | Pattern | Material Web base or key children |
126
+ | ------------ | ------- | --------------------------------- |
127
+ | `titanium-filled-duration-input` | extends | `MdFilledTextField` |
128
+ | `titanium-filled-youtube-input` | extends | `MdFilledTextField` |
129
+ | `titanium-filled-input-validator` | extends | `MdFilledField` |
130
+ | `titanium-filled-search-input` | composes | `md-filled-text-field` |
131
+ | `titanium-date-input` | composes | `md-filled-field` |
132
+ | `titanium-date-range-selector` | composes | `md-filled-field`, `md-menu`, `md-list` |
133
+ | `titanium-single-select-base` and all `leavitt-*-select`, `titanium-icon-picker`, `google-address-input` | composes | `md-filled-text-field`, `md-menu`, `md-menu-item` |
134
+ | `titanium-chip-multi-select` | composes | `titanium-filled-input-validator` (extends `MdFilledField`); slots intended for `md-filled-tonal-button`, `md-input-chip` |
135
+ | `titanium-confirmation-dialog` and most modal/dialog components | composes | `md-dialog` (+ `md-filled-button` / `md-text-button` actions) |
136
+ | `titanium-data-table-core` | composes | `md-checkbox`, `md-icon-button`, `md-menu` |
137
+ | `titanium-page-control` | composes | `md-filled-select` |
138
+ | `titanium-chip` | composes | `md-ripple`, `md-focus-ring` (custom chip — not `md-chip`) |
139
+ | Snackbars, toolbars, many app-shell pieces | composes | assorted `md-icon`, `md-icon-button`, `md-filled-button`, etc. |
140
+
141
+ When a component **extends** `MdFilledTextField` or `MdFilledField`, consumers can use standard Material field properties on the titanium tag: `label`, `error`, `error-text`, `supporting-text`, `required`, `disabled`, `prefix-text`, `suffix-text`, `checkValidity()`, `reportValidity()`, `setCustomValidity()`, leading/trailing icon slots, and documented CSS parts.
142
+
143
+ ### Styling
144
+
145
+ Material Web theming uses **CSS custom properties** on `:host` or on a child `md-*` selector:
146
+
147
+ ```css
148
+ :host {
149
+ --md-filled-text-field-container-shape: 24px;
150
+ --md-filled-text-field-container-color: var(--md-sys-color-surface-container-high);
151
+ --md-sys-color-primary: /* app theme */;
152
+ }
153
+ ```
154
+
155
+ - `--md-sys-color-*` — Material 3 color roles (surface, on-surface, primary, outline-variant, …)
156
+ - `--md-filled-text-field-*`, `--md-filled-field-*`, `--md-dialog-*`, `--md-icon-button-*`, … — per-component tokens
157
+
158
+ Set tokens on the titanium host when the inner `md-*` element inherits from `:host`, or target the child directly (e.g. `md-filled-text-field { … }` in the component's `static styles`). See `titanium-filled-search-input` and `titanium-drawer` for examples.
159
+
160
+ ### Internal utilities from Material Web
161
+
162
+ Some titanium components import Material Web **internals** (not part of the public consumer API):
163
+
164
+ - `redispatchEvent` from `@material/web/internal/events/redispatch-event` — re-bubbles native/MW events from composed children
165
+ - `stringConverter` and field internals — used by `titanium-date-input` for form-associated behavior
166
+
167
+ Do not import `@material/web/internal/*` from consuming applications unless Material Web documents those paths as stable.
168
+
169
+ ## Tag naming caveats
170
+
171
+ Several elements omit the `titanium-` / `leavitt-` prefix:
172
+
173
+ `profile-picture`, `profile-picture-menu`, `report-a-problem-dialog`, `provide-feedback-dialog`, `google-address-input`, `manual-address-dialog`, `crop-and-save-image-dialog`, `image-preview-dialog`, `file-list-item`, `folder-list-item`, `data-table-core-settings-sort-item`
174
+
175
+ ## Cross-cutting patterns
176
+
177
+ ### Loading — `promiseTracking`
178
+
179
+ ```ts
180
+ import { promiseTracking } from '@leavittsoftware/web/titanium/helpers/promise-tracking';
181
+
182
+ @promiseTracking('trackSavingPromise')
183
+ @state() accessor isSaving = false;
184
+ declare trackSavingPromise: (promise: Promise<unknown>) => Promise<void>;
185
+
186
+ async #save() {
187
+ const post = api.postAsync('...', dto);
188
+ this.trackSavingPromise(post);
189
+ await post;
190
+ }
191
+ ```
192
+
193
+ Supports multiple independent flags per component (`isSaving` vs `isDeleting`). Re-entrant: flag stays true until all concurrent promises settle.
194
+
195
+ ### Ancestor loading — `PendingStateEvent`
196
+
197
+ ```ts
198
+ import { PendingStateEvent } from '@leavittsoftware/web/titanium/types/pending-state-event';
199
+
200
+ const promise = api.getAsync('...');
201
+ this.dispatchEvent(new PendingStateEvent(promise));
202
+ await promise;
203
+ ```
204
+
205
+ Bubbles and is composed. Wire `.pendingStateElement=${this}` on `leavitt-app-main-content-container` or `titanium-circle-loading-indicator`. The circle indicator calls `stopPropagation()` so drawer events don't light sibling containers.
206
+
207
+ Use **either** a local spinner **or** `PendingStateEvent` for the same promise — not both unless intentional (e.g. `isLoading` gates empty state while circle shows overlay).
208
+
209
+ ### Toasts — `ShowSnackbarEvent`
210
+
211
+ ```ts
212
+ import { ShowSnackbarEvent } from '@leavittsoftware/web/titanium/snackbar/show-snackbar-event';
213
+
214
+ this.dispatchEvent(new ShowSnackbarEvent('Saved successfully'));
215
+ this.dispatchEvent(new ShowSnackbarEvent(httpError)); // HttpError object
216
+ ```
217
+
218
+ Place `<titanium-snackbar-stack>` in the app shell; it listens for `show-snackbar` on `document` or a custom `.eventListenerTarget`.
219
+
220
+ ### URL filters — `FilterController`
221
+
222
+ ```ts
223
+ import { FilterController } from '@leavittsoftware/web/titanium/data-table/filter-controller';
224
+
225
+ filterController = new FilterController('my-page-route');
226
+ // Register Filter instances; syncs with query string; getActiveFilterOdata() for OData $filter
227
+ ```
228
+
229
+ The constructor `path` should match the page's route path.
230
+
231
+ ### Site search — `TitaniumSiteSearchTextFieldController`
232
+
233
+ ```ts
234
+ import { TitaniumSiteSearchTextFieldController } from '@leavittsoftware/web/titanium/site-search-text-field-controller/site-search-text-field-controller';
235
+
236
+ searchController = new TitaniumSiteSearchTextFieldController(this, siteSearchTextFieldContext, {
237
+ placeholder: 'Search...',
238
+ onSearch: () => this.#reload(),
239
+ });
240
+ ```
241
+
242
+ Page host must expose `isActive: boolean`. Read `this.searchController.searchTerm` when fetching. Toggle `this.searchController.disabled` when search requires a prerequisite (e.g. company selected).
243
+
244
+ ### OData HTTP — `ApiService`
245
+
246
+ Required by `leavitt-*-select`, `leavitt-file-explorer`, email viewers. Pass a configured singleton:
247
+
248
+ ```ts
249
+ import ApiService from '@leavittsoftware/web/leavitt/api-service/api-service';
250
+
251
+ const result = await apiService.getAsync<Partial<Person>>('People/?$top=25&$count=true');
252
+ const items = result.toList();
253
+ const total = result.odataCount;
254
+ ```
255
+
256
+ ### Typography styles
257
+
258
+ Always import shared text styles before custom font sizing:
259
+
260
+ ```ts
261
+ import { h2, h5, p } from '@leavittsoftware/web/titanium/styles/styles.js';
262
+
263
+ static styles = [h2, h5, p, css`...`];
264
+ ```
265
+
266
+ Exports: `h1`–`h5` (aliases `heading1`–`heading5`), `p`/`paragraph`, `a`/`link`, `dataRow`, `ellipsis`, `niceBadgeStyles` (from `titanium/styles/nice-badge`).
267
+
268
+ ### Status pills in data tables
269
+
270
+ `titanium-data-table-core` injects `data-table-content-styles`. In column `render` functions:
271
+
272
+ ```html
273
+ <span indicator>Neutral</span>
274
+ <span indicator green>Active</span>
275
+ <span indicator red>Inactive</span>
276
+ ```
277
+
278
+ Only green / red / neutral-gray — no orange/warning tier.
279
+
280
+ ### Multi-word HTML attributes
281
+
282
+ Use kebab-case for reflected attributes: `local-storage-key="my-key"`, not `localStorageKey="my-key"`. Use `.localStorageKey=${value}` only for dynamic property binding.
283
+
284
+ ---
285
+
286
+ ## Data tables
287
+
288
+ Use `titanium-data-table-core` + `titanium-data-table-action-bar` + `titanium-page-control` together for list pages.
289
+
290
+ Define columns via `TitaniumDataTableCoreMetaData<T>`:
291
+
292
+ ```ts
293
+ tableMetaData: TitaniumDataTableCoreMetaData<MyItem> = {
294
+ uniqueKey: (item) => String(item.Id),
295
+ itemLinkUrl: (item) => `/items/${item.Id}`,
296
+ itemMetaData: [{ key: 'name', friendlyName: 'Name', render: (item) => html`${item.Name}`, getSortExpression: () => 'Name' }],
297
+ reorderConfig: { sortPropertyKey: 'SortOrder', reorderItemDisplayKey: 'Name' },
298
+ };
299
+ ```
300
+
301
+ - Sort/column prefs persist in `localStorage` under `local-storage-key`
302
+ - `friendlyName` values: sentence case
303
+ - Reorder: listen `@reorder-save-request`, call `e.detail.resolve()` on success or `e.detail.reject(error)` on failure — the reorder dialog shows errors via its own snackbar; don't snackbar in the save handler
304
+ - On refetch: don't clear rows (layout jank); use `trackLoadingPromise` / `disabled` on the table
305
+ - Dispatches `change-route` (composed) when row has `itemLinkUrl`; `items-reordered` when reorder dialog applies
306
+
307
+ ## Inheritance bases
308
+
309
+ Titanium class inheritance (in addition to Material Web extend/compose — see **Material Web foundation**):
310
+
311
+ | Base | Path | Used by |
312
+ | -------------------------- | ------------------------------------------------ | --------------------------------------------------------------------------- |
313
+ | `MdFilledTextField` | `@material/web/textfield/filled-text-field` | `titanium-filled-duration-input`, `titanium-filled-youtube-input` |
314
+ | `MdFilledField` | `@material/web/field/filled-field` | `titanium-filled-input-validator` |
315
+ | `TitaniumSingleSelectBase` | `titanium/single-select-base/single-select-base` | All `leavitt-*-select`, `titanium-icon-picker`, `google-address-input` |
316
+ | `google-address-input` | `titanium/address-input/google-address-input` | `titanium-address-input` |
317
+ | `ThemePreference` mixin | `leavitt/theme/theme-preference` | `leavitt-app-logo`, `leavitt-error-page`, `leavitt-service-worker-notifier`, `titanium-single-select-base` |
318
+
319
+ ---
320
+
321
+ # Components
322
+
323
+ ## App shell
324
+
325
+ ### `leavitt-app-main-content-container`
326
+
327
+ **Purpose:** Main scrollable content area with loading overlay; adapts layout when main menu is in drawer mode.
328
+
329
+ **Import:** `import '@leavittsoftware/web/leavitt/app/app-main-content-container.js'`
330
+
331
+ **Source:** `leavitt/app/app-main-content-container.ts`
332
+
333
+ | Kind | Name | Type / values | Notes |
334
+ | --------- | ---------------------- | ----------------- | ----------------------------------------------------------------------- |
335
+ | Attribute | `main-menu-position` | `string` | From `mainMenuPositionContext`; `'drawer'` changes height/border-radius |
336
+ | Property | `.pendingStateElement` | `Element \| null` | Host that dispatches `PendingStateEvent` for the circle loader |
337
+
338
+ **Methods:** —
339
+
340
+ **Events:** Listens for `pending-state` on `pendingStateElement`
341
+
342
+ **Slots:** default
343
+
344
+ **CSS parts:** `loading-indicator`, `scroll-container`
345
+
346
+ **Usage notes / gotchas:**
347
+
348
+ - Height is `100dvh`-based; drawer mode removes right margin and border radius
349
+ - When kicking off async work in `updated()`, `await this.appMainContentContainer?.updateComplete` first so the loader can appear
350
+
351
+ **Pairs with:** `titanium-circle-loading-indicator`, `mainMenuPositionContext`
352
+
353
+ ---
354
+
355
+ ### `leavitt-app-navigation-header`
356
+
357
+ **Purpose:** Sticky app header with up to 5-level breadcrumb trail.
358
+
359
+ **Import:** `import '@leavittsoftware/web/leavitt/app/app-navigation-header.js'`
360
+
361
+ **Source:** `leavitt/app/app-navigation-header.ts`
362
+
363
+ | Kind | Name | Type / values | Notes |
364
+ | --------- | --------------------------- | ----------------- | ---------------------- |
365
+ | Attribute | `sticky-top` | `boolean` | Reflected |
366
+ | Property | `scrollable-parent` | `Element \| null` | Auto-detected if unset |
367
+ | Property | `level1Text` … `level5Text` | `string \| null` | Breadcrumb labels |
368
+ | Property | `level1Href` … `level5Href` | `string \| null` | Breadcrumb links |
369
+
370
+ **Methods:** —
371
+
372
+ **Events:** —
373
+
374
+ **Slots:** `trailing`, `footer`
375
+
376
+ **CSS parts:** `main`, `trailing`, `footer`
377
+
378
+ **Usage notes / gotchas:**
379
+
380
+ - Border appears when scrolled + `sticky-top`
381
+ - Top-level nav labels should match page header text (level1 = top nav, level3 = sub-nav)
382
+
383
+ ---
384
+
385
+ ### `leavitt-app-navigation-footer`
386
+
387
+ **Purpose:** Sticky bottom bar with leading/default/trailing action areas.
388
+
389
+ **Import:** `import '@leavittsoftware/web/leavitt/app/app-navigation-footer.js'`
390
+
391
+ **Source:** `leavitt/app/app-navigation-footer.ts`
392
+
393
+ | Kind | Name | Type / values | Notes |
394
+ | --------- | ------------------- | ----------------- | ----------------- |
395
+ | Attribute | `max-width` | `string` | Default `'640px'` |
396
+ | Property | `scrollable-parent` | `Element \| null` | |
397
+
398
+ **Methods:** `isOverflown(element: Element): boolean`
399
+
400
+ **Events:** —
401
+
402
+ **Slots:** `leading`, default, `trailing`
403
+
404
+ **CSS parts:** `main`, `leading`, `trailing`
405
+
406
+ **Usage notes / gotchas:** Shows top border when scroll parent overflows
407
+
408
+ ---
409
+
410
+ ### `leavitt-app-logo`
411
+
412
+ **Purpose:** App name + Leavitt Group mark with hover animation; theme-aware CDN mark.
413
+
414
+ **Import:** `import '@leavittsoftware/web/leavitt/app/app-logo.js'`
415
+
416
+ **Source:** `leavitt/app/app-logo.ts`
417
+
418
+ | Kind | Name | Type / values | Notes |
419
+ | --------- | ---------- | ---------------- | -------------------------------- |
420
+ | Property | `href` | `string` | Default `'/'` |
421
+ | Property | `title` | `string` | |
422
+ | Attribute | `app-name` | `string \| null` | Title case; acronyms keep casing |
423
+
424
+ **Methods / events / slots / parts:** —
425
+
426
+ **Usage notes / gotchas:** Extends `ThemePreference` mixin; mark URL switches for dark theme
427
+
428
+ ---
429
+
430
+ ### `leavitt-app-width-limiter`
431
+
432
+ **Purpose:** Centers content with configurable max width.
433
+
434
+ **Import:** `import '@leavittsoftware/web/leavitt/app/app-width-limiter.js'`
435
+
436
+ **Source:** `leavitt/app/app-width-limiter.ts`
437
+
438
+ | Kind | Name | Type / values | Notes |
439
+ | --------- | ----------- | ------------- | ----------------- |
440
+ | Attribute | `max-width` | `string` | Default `'640px'` |
441
+
442
+ **Slots:** default
443
+
444
+ **Usage notes / gotchas:** Parent must be `display: grid` or `display: flex` for `justify-self: center` to work (Firefox)
445
+
446
+ ---
447
+
448
+ ## Layout and navigation
449
+
450
+ ### `titanium-drawer`
451
+
452
+ **Purpose:** Fly-out / inline drawer based on native `<dialog>`.
453
+
454
+ **Import:** `import '@leavittsoftware/web/titanium/drawer/drawer.js'`
455
+
456
+ **Source:** `titanium/drawer/drawer.ts`
457
+
458
+ | Kind | Name | Type / values | Notes |
459
+ | --------- | --------------------------------- | --------------------- | ----------------------------------------------- |
460
+ | Attribute | `mode` | `inline` \| `flyover` | |
461
+ | Attribute | `open` | `boolean` | Read-only; reflected from `isOpen` |
462
+ | Attribute | `direction` | `ltr` \| `rtl` | Animation direction |
463
+ | Attribute | `fixed` | `boolean` | Content position when closed (inline mode only) |
464
+ | Attribute | `keep-open-when-going-to-flyover` | `boolean` | Preserve open state on mode switch |
465
+
466
+ **Methods:** `open()`, `close()`, `toggle()`, `closeQuick()`
467
+
468
+ **Events:** `open-change`; redispatches native `close`, `toggle` from dialog
469
+
470
+ **Slots:** default, `header`, `footer`
471
+
472
+ **CSS parts:** `dialog`, `header`, `main`, `footer`
473
+
474
+ **CSS custom properties:** `--md-sys-color-outline-variant`, `--md-sys-color-on-background`
475
+
476
+ **Usage notes / gotchas:**
477
+
478
+ - Swipe-left closes; backdrop click closes; `popstate` closes dialog
479
+ - Flyover sets `html { overflow: hidden }`
480
+ - Inline sidebar: set `mode="inline"` and call `open()`; do not use removed `always-show-content`
481
+ - Switching `mode` from inline→flyover: use `keep-open-when-going-to-flyover` to stay open
482
+
483
+ ---
484
+
485
+ ### `titanium-toolbar`
486
+
487
+ **Purpose:** Fixed top Material toolbar with scroll-based elevation.
488
+
489
+ **Import:** `import '@leavittsoftware/web/titanium/toolbar/toolbar.js'`
490
+
491
+ **Source:** `titanium/toolbar/toolbar.ts`
492
+
493
+ | Kind | Name | Type / values | Notes |
494
+ | --------- | -------- | ------------- | --------------------------- |
495
+ | Attribute | `shadow` | `boolean` | Auto-set on document scroll |
496
+
497
+ **Slots:** default (style slotted `[main-title]`)
498
+
499
+ **Usage notes / gotchas:** Listens on `document` scroll, not a local container
500
+
501
+ ---
502
+
503
+ ### `titanium-show-hide`
504
+
505
+ **Purpose:** Collapsible overflow content with optional fade and custom toggle button.
506
+
507
+ **Import:** `import '@leavittsoftware/web/titanium/show-hide/show-hide.js'`
508
+
509
+ **Source:** `titanium/show-hide/show-hide.ts`
510
+
511
+ | Kind | Name | Type / values | Notes |
512
+ | --------- | ----------------- | ------------- | -------------------------------- |
513
+ | Attribute | `collapse-height` | `number` | Default `120` |
514
+ | Attribute | `collapsed` | `boolean` | |
515
+ | Attribute | `disable-fade` | `boolean` | |
516
+ | Property | `hiddenItemCount` | `number` | Read-only count of clipped items |
517
+
518
+ **Events:** `collapsed-changed`, `hidden-item-count-changed`
519
+
520
+ **Slots:** default (direct children), `button`
521
+
522
+ **CSS parts:** `items-container`, `button`
523
+
524
+ **Usage notes / gotchas:** ResizeObserver counts clipped children; default button hidden when nothing is hidden
525
+
526
+ ---
527
+
528
+ ### `titanium-collapsible-container`
529
+
530
+ **Purpose:** Expand/collapse panel with header slot.
531
+
532
+ **Import:** `import '@leavittsoftware/web/titanium/collapsible-container/collapsible-container.js'`
533
+
534
+ **Source:** `titanium/collapsible-container/collapsible-container.ts`
535
+
536
+ | Kind | Name | Type / values | Notes |
537
+ | --------- | -------------------- | ------------- | --------- |
538
+ | Attribute | `opened`, `disabled` | `boolean` | Reflected |
539
+
540
+ **Slots:** `header`, `content`
541
+
542
+ **CSS parts:** `button`, `main`
543
+
544
+ **Usage notes / gotchas:** Toggles `opened` on header button click
545
+
546
+ ---
547
+
548
+ ## Data tables
549
+
550
+ ### `titanium-data-table-core`
551
+
552
+ **Purpose:** Metadata-driven table with sort, column picker, CSV export, selection, and optional reorder.
553
+
554
+ **Import:** `import '@leavittsoftware/web/titanium/data-table/data-table-core.js'`
555
+
556
+ **Source:** `titanium/data-table/data-table-core.ts`
557
+
558
+ | Kind | Name | Type / values | Notes |
559
+ | --------- | --------------------------- | ------------------------------------------ | ---------------------------------------------- |
560
+ | Property | `items` | `T[]` | Current rows |
561
+ | Property | `tableMetaData` | `TitaniumDataTableCoreMetaData<T> \| null` | Column config |
562
+ | Property | `selected` | `T[]` | Selected rows |
563
+ | Attribute | `selection-mode` | `single` \| `multi` \| `none` | Default `none` |
564
+ | Attribute | `local-storage-key` | `string` | Default `'dtc-pref'`; **kebab-case attribute** |
565
+ | Attribute | `sticky-header`, `disabled` | `boolean` | |
566
+ | Property | `sort` | getter/setter | Persists to `{key}-user-sort` in localStorage |
567
+ | Property | `userSettings` | getter/setter | Column visibility prefs |
568
+ | Property | `supplementalItemStyles` | `CSSResult \| CSSResultGroup \| null` | Per-row styles |
569
+ | State | `isLoading` | `boolean` | Via `promiseTracking` |
570
+
571
+ **Methods:** `selectAll()`, `deselectAll()`, `resetSort()`, `trackLoadingPromise(promise)`, `loadWhile(promise)` (deprecated alias for `trackLoadingPromise`)
572
+
573
+ **Events:**
574
+
575
+ - `selected-changed` (composed)
576
+ - `sort-changed`
577
+ - `items-reordered` — `CustomEvent<T[]>`
578
+ - `change-route` — `CustomEvent<{ path: string }>` (composed)
579
+ - `reorder-save-request` — delegate with `resolve()` / `reject(error)`
580
+
581
+ **Slots:** `settings-menu-items`
582
+
583
+ **CSS parts:** `table`
584
+
585
+ **Exported types:** `TitaniumDataTableCoreMetaData`, `TitaniumDataTableCoreItemMetaData`, `TitaniumDataTableCoreSortItem`, `generateDefaultSortFromMetaData()`
586
+
587
+ **Usage notes / gotchas:**
588
+
589
+ - Clears selection when `items` reference changes
590
+ - Don't clear rows on refetch (scrollbar jank); disable controls via `disabled` / `isLoading`
591
+ - Status columns: use `<span indicator green>` / `red` (see cross-cutting patterns)
592
+ - `friendlyName` on column metadata: sentence case
593
+
594
+ ---
595
+
596
+ ### `titanium-data-table-action-bar`
597
+
598
+ **Purpose:** Filter/add button bar that swaps to bulk selection actions when rows are selected.
599
+
600
+ **Import:** `import '@leavittsoftware/web/titanium/data-table/data-table-action-bar.js'`
601
+
602
+ **Source:** `titanium/data-table/data-table-action-bar.ts`
603
+
604
+ | Kind | Name | Type / values | Notes |
605
+ | -------- | ---------- | -------------- | ----- |
606
+ | Property | `selected` | `Partial<T>[]` | |
607
+
608
+ **Slots:** `add-button`, `filters`, `selected-actions`
609
+
610
+ **CSS parts:** `main`, `add-button-container`, `filters-container`, `selected-action-veil`, `selected-action-title`, `action-container`
611
+
612
+ **Pairs with:** `titanium-data-table-core`
613
+
614
+ ---
615
+
616
+ ### `titanium-page-control`
617
+
618
+ **Purpose:** Page size selector + prev/next paging control.
619
+
620
+ **Import:** `import '@leavittsoftware/web/titanium/data-table/page-control.js'`
621
+
622
+ **Source:** `titanium/data-table/page-control.ts`
623
+
624
+ | Kind | Name | Type / values | Notes |
625
+ | --------- | ------------------- | ------------- | -------------------------------------------- |
626
+ | Property | `pageSizes` | `number[]` | Default `[10,15,20,50]` |
627
+ | Attribute | `default-page-size` | `number` | Default `10` |
628
+ | Property | `page` | `number` | Zero-based |
629
+ | Property | `count` | `number` | Total items |
630
+ | Attribute | `local-storage-key` | `string` | **Required** for `take` persistence |
631
+ | Property | `label` | `string` | Default `'Items per page'` |
632
+ | Property | `disabled` | `boolean` | |
633
+ | Property | `take` | getter/setter | Persists to localStorage; resets `page` to 0 |
634
+
635
+ **Events:** `action` (composed) — fired on page or take change
636
+
637
+ **Usage notes / gotchas:**
638
+
639
+ - Unknown `take` values are added to `pageSizes`
640
+ - Next disabled when `(page+1)*take >= count`
641
+ - Use `local-storage-key` attribute (kebab-case), not `localStorageKey`
642
+
643
+ ---
644
+
645
+ ## Form inputs
646
+
647
+ ### `titanium-date-input`
648
+
649
+ **Purpose:** Cross-browser date / datetime-local input (form-associated).
650
+
651
+ **Material Web:** Composes `md-filled-field`; uses MW field tokens and `redispatchEvent` for form events.
652
+
653
+ **Import:** `import '@leavittsoftware/web/titanium/date-input/date-input.js'`
654
+
655
+ | Kind | Name | Type / values | Notes |
656
+ | --------- | ------------------------------------------------------ | -------------------------- | ----- |
657
+ | Property | `value`, `label`, `placeholder`, `supporting-text` | `string` | |
658
+ | Attribute | `type` | `date` \| `datetime-local` | |
659
+ | Attribute | `min`, `max`, `maxLength` | `string` | |
660
+ | Property | `required`, `disabled`, `error` | `boolean` | |
661
+ | Property | `prefix-text`, `suffix-text` | `string` | |
662
+ | Attribute | `has-leading-icon`, `has-trailing-icon`, `no-asterisk` | `boolean` | |
663
+
664
+ **Methods:** `checkValidity()`, `reportValidity()`, `select()`, `setCustomValidity()`, `reset()`
665
+
666
+ **Events:** Redispatches `change`, `blur`, `select`, `invalid`
667
+
668
+ **Slots:** `leading-icon`, `trailing-icon` (default trailing: calendar picker)
669
+
670
+ **Usage notes / gotchas:** `formAssociated`; after user input, attribute `value` no longer syncs until `reset()`; Safari/iOS/Firefox-specific styling
671
+
672
+ ---
673
+
674
+ ### `titanium-date-range-selector`
675
+
676
+ **Purpose:** Preset + custom date range picker with popover UI.
677
+
678
+ **Material Web:** Composes `md-filled-field`, `md-menu`, `md-list`, `md-text-button`.
679
+
680
+ **Import:** `import '@leavittsoftware/web/titanium/date-range-selector/date-range-selector.js'`
681
+
682
+ | Kind | Name | Type / values | Notes |
683
+ | -------- | ---------------------------------- | -------------------------------------- | ------------------------------------------------------------- |
684
+ | Property | `range` | `string` | Default `'custom'`; presets like `allTime`, `last7Days`, etc. |
685
+ | Property | `startDate`, `endDate` | `string` | ISO date strings |
686
+ | Property | `label`, `supporting-text`, `type` | `string` | `type`: `date` \| `datetime-local` |
687
+ | Property | `customDateRanges` | `Map<string, DateRangeOption> \| null` | Override presets |
688
+ | Property | `disabled`, `positioning` | | `positioning`: `popover` \| `fixed` |
689
+
690
+ **Methods:** `reset()` — sets `range` to `'allTime'`
691
+
692
+ **Events:** `change` on Set (note: `DateRangeChangedEvent` class exists but component fires `change`)
693
+
694
+ **CSS parts:** `field`
695
+
696
+ **Usage notes / gotchas:** Firefox lacks `popover` → falls back to `fixed`; datetime custom requires 16-char values to enable Set
697
+
698
+ ---
699
+
700
+ ### `titanium-filled-duration-input`
701
+
702
+ **Purpose:** Natural-language duration input (parses strings like "3 hours and 30 minutes").
703
+
704
+ **Material Web:** Extends `MdFilledTextField` — inherits label, validation, and field styling API.
705
+
706
+ **Import:** `import '@leavittsoftware/web/titanium/duration-input/filled-duration-input.js'`
707
+
708
+ **Source:** `titanium/duration-input/filled-duration-input.ts`
709
+
710
+ | Kind | Name | Type / values | Notes |
711
+ | -------- | ---------------------- | ------------------------ | --------------- |
712
+ | Property | `duration` | `dayjs.Duration \| null` | Canonical value |
713
+ | Property | `label`, `placeholder` | `string` | |
714
+
715
+ **Events:** `duration-change` — read `event.target.duration`
716
+
717
+ **Usage notes / gotchas:** `value` is human-readable string; use `duration` property for logic
718
+
719
+ ---
720
+
721
+ ### `titanium-filled-search-input`
722
+
723
+ **Purpose:** Full-width filled search field with clear button.
724
+
725
+ **Material Web:** Composes `md-filled-text-field`; style via `--md-filled-text-field-*` on `:host`.
726
+
727
+ **Import:** `import '@leavittsoftware/web/titanium/search-input/filled-search-input.js'`
728
+
729
+ **Source:** `titanium/search-input/filled-search-input.ts`
730
+
731
+ | Kind | Name | Type / values | Notes |
732
+ | -------- | ---------------------------------------- | ------------- | ----- |
733
+ | Property | `value`, `placeholder` | `string` | |
734
+ | Property | `disabled`, `autocomplete`, `spellcheck` | | |
735
+
736
+ **Events:** `input` (composed); redispatches `blur`, `focus`, `change`, `invalid`
737
+
738
+ ---
739
+
740
+ ### `titanium-filled-youtube-input`
741
+
742
+ **Purpose:** YouTube video key input; strips full URLs to 11-char key; shows thumbnail preview.
743
+
744
+ **Material Web:** Extends `MdFilledTextField` — inherits label, validation, and trailing-icon slot.
745
+
746
+ **Import:** `import '@leavittsoftware/web/titanium/youtube-input/filled-youtube-input.js'`
747
+
748
+ **Source:** `titanium/youtube-input/filled-youtube-input.ts`
749
+
750
+ | Kind | Name | Type / values | Notes |
751
+ | -------- | ---------------- | ------------- | ------------------- |
752
+ | Property | `value`, `label` | `string` | |
753
+ | Property | `pattern` | `string` | Default `'^.{11}$'` |
754
+
755
+ **Methods:** `reset()`
756
+
757
+ **Usage notes / gotchas:** Shows thumbnail in trailing slot when key length is 11
758
+
759
+ ---
760
+
761
+ ### `titanium-address-input`
762
+
763
+ **Purpose:** Google Places search with manual address entry fallback.
764
+
765
+ **Import:** `import '@leavittsoftware/web/titanium/address-input/address-input.js'`
766
+
767
+ | Kind | Name | Type / values | Notes |
768
+ | --------- | ------------------------------------ | ------------- | ------------------------ |
769
+ | Property | `googleMapsApiKey` | `string` | **Required** (inherited) |
770
+ | Attribute | `show-street2`, `show-county` | `boolean` | |
771
+ | Property | `allow-international` | `boolean` | |
772
+ | + | All `TitaniumSingleSelectBase` props | | |
773
+
774
+ **Events:** `selected` (JSDoc says `location-changed` but base fires `selected`)
775
+
776
+ **Usage notes / gotchas:** Requires Google Maps API key; opens `manual-address-dialog` for manual entry; US street validation when not international
777
+
778
+ ---
779
+
780
+ ### `google-address-input`
781
+
782
+ **Purpose:** Google Places autocomplete base (extended by `titanium-address-input`).
783
+
784
+ **Import:** `import '@leavittsoftware/web/titanium/address-input/google-address-input.js'`
785
+
786
+ | Kind | Name | Type / values | Notes |
787
+ | -------- | --------------------- | ------------- | ------------------------------ |
788
+ | Property | `googleMapsApiKey` | `string` | **Required** |
789
+ | Property | `pathToSelectedText` | `string` | Default `'primaryDisplayText'` |
790
+ | Property | `allow-international` | `boolean` | |
791
+
792
+ **Events:** `selected`; dispatches `ShowSnackbarEvent` on API errors
793
+
794
+ **Usage notes / gotchas:** Loads Google Maps via `@googlemaps/js-api-loader` v2, which reads `process.env.NODE_ENV` at module scope. The component ships a guarded global `process` shim (`google-maps-process-shim.ts`, imported first) so it works in browser environments that don't define `process`; consumers need no bundler define. **GCP:** the API key's project must have **Maps JavaScript API** and **Places API (New)** (`places.googleapis.com`) enabled — the legacy Places API alone is not sufficient after the `AutocompleteSuggestion` / `Place` migration. A `SERVICE_DISABLED` error means Places API (New) is missing on that project.
795
+
796
+ ---
797
+
798
+ ### `titanium-chip`
799
+
800
+ **Purpose:** Custom chip (link, filter, or input-chip with remove).
801
+
802
+ **Import:** `import '@leavittsoftware/web/titanium/chip/chip.js'`
803
+
804
+ | Kind | Name | Type / values | Notes |
805
+ | --------- | ----------------------------------------- | ------------- | --------------------- |
806
+ | Property | `label` | `string` | |
807
+ | Property | `selected`, `disabled`, `non-interactive` | `boolean` | |
808
+ | Property | `href`, `download`, `target` | `string` | Link chip |
809
+ | Attribute | `input-chip` | `boolean` | Enables remove button |
810
+
811
+ **Events:** `remove` (when `input-chip`)
812
+
813
+ **Slots:** `icon`, `label`, `trailing`
814
+
815
+ **CSS parts:** `button`, `ripple`, `focus-ring`
816
+
817
+ **Usage notes / gotchas:** Use `filled` attribute in consuming apps for design consistency
818
+
819
+ ---
820
+
821
+ ### `titanium-chip-multi-select`
822
+
823
+ **Purpose:** Filled validator wrapper for slotted chips + add button.
824
+
825
+ **Import:** `import '@leavittsoftware/web/titanium/chip-multi-select/chip-multi-select.js'`
826
+
827
+ | Kind | Name | Type / values | Notes |
828
+ | -------- | -------------------------------------------------------- | ------------- | ----- |
829
+ | Property | `label`, `noItemsText`, `supportingText`, `errorText` | `string` | |
830
+ | Property | `required`, `hasItems`, `error`, `resizable`, `disabled` | `boolean` | |
831
+
832
+ **Methods:** `checkValidity()`, `reportValidity()`, `reset()`
833
+
834
+ **Slots:** default — intended: `md-filled-tonal-button` + `md-input-chip` / chips
835
+
836
+ **Usage notes / gotchas:** `disabled` on host does not auto-disable slotted chips/buttons
837
+
838
+ ---
839
+
840
+ ### `titanium-single-select-base`
841
+
842
+ **Purpose:** Generic autocomplete single-select base (extended by domain selects).
843
+
844
+ **Material Web:** Composes `md-filled-text-field` + `md-menu`; `required` patches inner `md-filled-text-field.checkValidity`.
845
+
846
+ **Import:** `import '@leavittsoftware/web/titanium/single-select-base/single-select-base.js'`
847
+
848
+ | Kind | Name | Type / values | Notes |
849
+ | --------- | ------------------------------------------------------ | -------------------- | ---------------------------------- |
850
+ | Property | `label`, `placeholder`, `selected` | | `selected`: `T \| null` |
851
+ | Property | `required`, `disabled`, `error`, `errorText` | `boolean` / `string` | |
852
+ | Property | `prefixText`, `suffixText`, `supportingText` | `string` | |
853
+ | Attribute | `no-asterisk`, `has-leading-icon`, `has-trailing-icon` | `boolean` | |
854
+ | Property | `pathToSelectedText` | `string` | Key on selected object for display |
855
+ | Property | `positioning`, `match-input-width`, `large`, `shaped` | | Menu positioning |
856
+ | Attribute | `disable-menu-open-on-focus` | `boolean` | |
857
+ | Attribute | `menu-open` | `boolean` | Reflected |
858
+ | State | `isLoading` | `boolean` | |
859
+
860
+ **Methods:** `reset()`, `softReset()`, `select()`, `focus()`, `checkValidity()`, `reportValidity()`, `setCustomValidity()`, `trackLoadingPromise()`
861
+
862
+ **Events:** `selected`; redispatches menu `opening`/`opened`/`closing`/`closed`
863
+
864
+ **Slots:** `leading-icon`, `trailing-icon`
865
+
866
+ **CSS parts:** `menu`
867
+
868
+ **Usage notes / gotchas:**
869
+
870
+ - `required` patches `md-filled-text-field.checkValidity`
871
+ - Custom validity when typed but not selected
872
+ - `positioning='popover'` falls back to `'fixed'` in Firefox
873
+
874
+ ---
875
+
876
+ ### `titanium-icon-picker`
877
+
878
+ **Purpose:** Material Symbols icon search and select.
879
+
880
+ **Import:** `import '@leavittsoftware/web/titanium/icon-picker/icon-picker.js'`
881
+
882
+ | Kind | Name | Type / values | Notes |
883
+ | -------- | -------------------------------- | ------------- | -------------------------- |
884
+ | Property | `favorites` | `string[]` | |
885
+ | Property | `whitelist` | `string` | Comma-separated icon names |
886
+ | Property | `pathToSelectedText` | `string` | Default `'icon'` |
887
+ | + | `TitaniumSingleSelectBase` props | | |
888
+
889
+ **Events:** `selected`
890
+
891
+ ---
892
+
893
+ ### `titanium-smart-attachment-input`
894
+
895
+ **Purpose:** File upload with chips, image crop, preview, optional delete confirmation.
896
+
897
+ **Import:** `import '@leavittsoftware/web/titanium/smart-attachment-input/smart-attachment-input.js'`
898
+
899
+ | Kind | Name | Type / values | Notes |
900
+ | --------- | ----------------------------------------------------------- | ---------------- | ----------------- |
901
+ | Property | `accept`, `multiple`, `required`, `disabled` | | |
902
+ | Property | `confirmDelete`, `confirmDeleteHeader`, `confirmDeleteText` | | |
903
+ | Property | `addButtonLabel`, `label`, `supportingText`, `noItemsText` | `string` | |
904
+ | Property | `options` | `CropperOptions` | Cropper.js config |
905
+ | Attribute | `force-png` | `boolean` | |
906
+
907
+ **Methods:** `getFiles()`, `setFiles(...)`, `setFilesFromDatabaseAttachments(...)`, `checkValidity()`, `reportValidity()`, `hasChanges()`, `reset()`, `handleNewFile(files)`
908
+
909
+ **Events:** `change`
910
+
911
+ **Usage notes / gotchas:** Uses internal `titanium-chip-multi-select`; revokes blob URLs on `reset()`
912
+
913
+ ---
914
+
915
+ ### `titanium-filled-input-validator`
916
+
917
+ **Purpose:** Filled MdField wrapper with custom `evaluator` validation function.
918
+
919
+ **Material Web:** Extends `MdFilledField` — slotted content sits inside the MW field container.
920
+
921
+ **Import:** `import '@leavittsoftware/web/titanium/input-validator/filled-input-validator.js'`
922
+
923
+ **Source:** `titanium/input-validator/filled-input-validator.ts`
924
+
925
+ | Kind | Name | Type / values | Notes |
926
+ | -------- | ------------- | ------------------------------------------------------------- | --------------------- |
927
+ | Property | `evaluator` | `() => boolean` | Custom validity check |
928
+ | Property | `populated` | `boolean` | |
929
+ | + | MdField props | `label`, `error`, `error-text`, `supporting-text`, `required` | |
930
+
931
+ **Methods:** `checkValidity()`, `reportValidity()`, `reset()`
932
+
933
+ **Slots:** default (wraps slotted input)
934
+
935
+ ---
936
+
937
+ ## Dialogs and confirmations
938
+
939
+ ### `titanium-confirmation-dialog`
940
+
941
+ **Purpose:** Imperative promise-based confirm/cancel dialog.
942
+
943
+ **Material Web:** Composes `md-dialog` with `md-text-button` / `md-filled-tonal-button` actions.
944
+
945
+ **Import:** `import '@leavittsoftware/web/titanium/confirmation-dialog/confirmation-dialog.js'`
946
+
947
+ | Kind | Name | Type / values | Notes |
948
+ | -------- | -------------------------------------------------- | ------------- | ----- |
949
+ | Property | `headline`, `text` | `string` | |
950
+ | Property | `confirmActionText`, `cancelActionText` | `string` | |
951
+ | Property | `disableConfirmationAction`, `disableCancelAction` | `boolean` | |
952
+
953
+ **Methods:** `open(headline, text)` → `Promise<'cancel' | 'confirmed'>`
954
+
955
+ **Slots:** default (content area)
956
+
957
+ **CSS parts:** `content-container`
958
+
959
+ ---
960
+
961
+ ### `provide-feedback-dialog`
962
+
963
+ **Purpose:** Modal to submit user feedback to Issue Tracking API.
964
+
965
+ **Import:** `import '@leavittsoftware/web/leavitt/user-feedback/provide-feedback-dialog.js'`
966
+
967
+ | Kind | Name | Type / values | Notes |
968
+ | -------- | -------------- | ----------------------- | ------------ |
969
+ | Property | `.userManager` | `AuthZeroLgUserManager` | **Required** |
970
+
971
+ **Methods:** `show()`, `reset()`
972
+
973
+ **Events:** `PendingStateEvent`, `ShowSnackbarEvent`
974
+
975
+ ---
976
+
977
+ ### `report-a-problem-dialog`
978
+
979
+ **Purpose:** Bug report modal with file attachments.
980
+
981
+ **Import:** `import '@leavittsoftware/web/leavitt/user-feedback/report-a-problem-dialog.js'`
982
+
983
+ | Kind | Name | Type / values | Notes |
984
+ | -------- | -------------- | ----------------------- | ------------ |
985
+ | Property | `.userManager` | `AuthZeroLgUserManager` | **Required** |
986
+
987
+ **Methods:** `show()`, `reset()`
988
+
989
+ **Uses:** `titanium-smart-attachment-input`, `titanium-snackbar-stack`
990
+
991
+ ---
992
+
993
+ ## Feedback, loading, and error pages
994
+
995
+ ### `titanium-snackbar-stack`
996
+
997
+ **Purpose:** Stackable snackbar host; listens for `ShowSnackbarEvent`.
998
+
999
+ **Import:** `import '@leavittsoftware/web/titanium/snackbar/snackbar-stack.js'`
1000
+
1001
+ | Kind | Name | Type / values | Notes |
1002
+ | -------- | ---------------------- | ------------------------- | ------------------ |
1003
+ | Property | `.eventListenerTarget` | `HTMLElement \| Document` | Default `document` |
1004
+
1005
+ **Methods:** `open(message, options?)`, `dismissAll()`
1006
+
1007
+ **Events listened:** `show-snackbar`
1008
+
1009
+ **Usage notes / gotchas:** `display: contents`; place in app shell or dialog (dialogs doing I/O need their own stack)
1010
+
1011
+ ---
1012
+
1013
+ ### `titanium-circle-loading-indicator`
1014
+
1015
+ **Purpose:** Scoped circular loading overlay on a pending-state element.
1016
+
1017
+ **Import:** `import '@leavittsoftware/web/titanium/circle-loading-indicator/circle-loading-indicator.js'`
1018
+
1019
+ | Kind | Name | Type / values | Notes |
1020
+ | -------- | ---------------------- | ----------------- | ----- |
1021
+ | Property | `.pendingStateElement` | `Element \| null` | |
1022
+
1023
+ **Events listened:** `pending-state`
1024
+
1025
+ **Usage notes / gotchas:**
1026
+
1027
+ - Sets parent `inert` while open; 75ms open delay, 400ms min visible
1028
+ - **Must** dispatch `PendingStateEvent` for work — `isLoading` alone does not drive the overlay
1029
+ - Calls `stopPropagation()` on the event
1030
+
1031
+ ---
1032
+
1033
+ ### `leavitt-error-page`
1034
+
1035
+ **Purpose:** Branded error page with an animated star background; theme-aware.
1036
+
1037
+ **Import:** `import '@leavittsoftware/web/leavitt/error-page/error-page.js'`
1038
+
1039
+ | Kind | Name | Type / values | Notes |
1040
+ | -------- | --------- | -------------------------- | ------------------ |
1041
+ | Property | `heading` | `string \| TemplateResult` | Default `'Hmm...'` |
1042
+ | Property | `message` | `string \| TemplateResult` | |
1043
+
1044
+ **Usage notes / gotchas:** Extends `ThemePreference`; renders a self-contained `<canvas>` starfield (no external particle dependency) that drifts and twinkles, honors `prefers-reduced-motion`, and recolors on theme change.
1045
+
1046
+ ---
1047
+
1048
+ ## Domain selects
1049
+
1050
+ All extend `TitaniumSingleSelectBase` and fire `selected`. All require `.apiService` (`ApiService` instance).
1051
+
1052
+ ### `leavitt-company-select`
1053
+
1054
+ **Import:** `import '@leavittsoftware/web/leavitt/company-select/company-select.js'`
1055
+
1056
+ | Kind | Name | Type / values | Notes |
1057
+ | -------- | ------------------- | -------------------- | --------------------------------- |
1058
+ | Property | `.apiService` | `ApiService` | **Required** |
1059
+ | Property | `apiControllerName` | `string` | Default `'Companies'` |
1060
+ | Property | `companies` | `Partial<Company>[]` | Preloaded list |
1061
+ | Property | `odataParts` | `string[]` | Default `orderby=Name,select=...` |
1062
+ | Property | `disableAutoLoad` | `boolean` | Skip auto-fetch on `firstUpdated` |
1063
+
1064
+ **Methods:** `reloadCompanies()`, `reset()` (inherited)
1065
+
1066
+ **Usage notes / gotchas:** Local Fuse.js search over preloaded companies; company mark icons in menu items
1067
+
1068
+ ---
1069
+
1070
+ ### `leavitt-person-select`
1071
+
1072
+ **Import:** `import '@leavittsoftware/web/leavitt/person-select/person-select.js'`
1073
+
1074
+ | Kind | Name | Type / values | Notes |
1075
+ | -------- | ------------------------ | ------------------- | ------------------------------------------ |
1076
+ | Property | `.apiService` | `ApiService` | **Required** |
1077
+ | Property | `apiControllerName` | `string` | |
1078
+ | Property | `odataParts` | `string[]` | |
1079
+ | Property | `searchType` | `local` \| `remote` | Remote uses OData `contains` on `FullName` |
1080
+ | Property | `enablePeoplePreloading` | `boolean` | |
1081
+ | Property | `people` | `Partial<Person>[]` | |
1082
+
1083
+ ---
1084
+
1085
+ ### `leavitt-person-company-select`
1086
+
1087
+ **Import:** `import '@leavittsoftware/web/leavitt/person-company-select/person-company-select.js'`
1088
+
1089
+ | Kind | Name | Type / values | Notes |
1090
+ | -------- | -------------------------- | ------------- | ------------ |
1091
+ | Property | `.apiService` | `ApiService` | **Required** |
1092
+ | Property | `peopleApiControllerName` | `string` | |
1093
+ | Property | `companyApiControllerName` | `string` | |
1094
+
1095
+ **Usage notes / gotchas:** Combined people + companies in one autocomplete
1096
+
1097
+ ---
1098
+
1099
+ ### `leavitt-person-group-select`
1100
+
1101
+ **Import:** `import '@leavittsoftware/web/leavitt/person-group-select/person-group-select.js'`
1102
+
1103
+ | Kind | Name | Type / values | Notes |
1104
+ | -------- | ------------------------- | ------------- | ------------ |
1105
+ | Property | `.apiService` | `ApiService` | **Required** |
1106
+ | Property | `peopleApiControllerName` | `string` | |
1107
+ | Property | `groupApiControllerName` | `string` | |
1108
+
1109
+ **Usage notes / gotchas:** Searches people and people groups
1110
+
1111
+ ---
1112
+
1113
+ ## File explorer
1114
+
1115
+ ### `leavitt-file-explorer`
1116
+
1117
+ **Purpose:** Full file/folder browser with grid/list views, upload, and admin actions.
1118
+
1119
+ **Import:** `import '@leavittsoftware/web/leavitt/file-explorer/file-explorer.js'`
1120
+
1121
+ | Kind | Name | Type / values | Notes |
1122
+ | --------- | --------------------------- | --------------------------------------------------- | ----------------------------- |
1123
+ | Property | `.apiService` | `ApiService` | **Required** |
1124
+ | Attribute | `file-explorer-id` | `number` | |
1125
+ | Attribute | `folder-id` | `number \| null` | |
1126
+ | Attribute | `local-storage-display-key` | `string` | Persists grid/list preference |
1127
+ | Attribute | `prevent-navigation-up` | `boolean` | |
1128
+ | Property | `display` | `grid` \| `list` | Persisted |
1129
+ | Property | `state` | `no-permission` \| `files` \| `no-files` \| `error` | |
1130
+
1131
+ **Methods:** `reload()`
1132
+
1133
+ **Events:** `folder-added`, `folder-deleted`, `file-added`, `file-deleted`, `PendingStateEvent`, `ShowSnackbarEvent`
1134
+
1135
+ **Usage notes / gotchas:**
1136
+
1137
+ - Subscribes to `fileExplorerEvents` bus for live modal sync
1138
+ - Uses `titanium-confirmation-dialog` for deletes
1139
+ - Internal modals: `leavitt-file-modal`, `leavitt-folder-modal`, `leavitt-add-folder-modal`
1140
+
1141
+ ---
1142
+
1143
+ ## Email history
1144
+
1145
+ ### `leavitt-email-history-viewer-filled`
1146
+
1147
+ **Purpose:** App-shell integrated email log viewer using `titanium-data-table-core`.
1148
+
1149
+ **Import:** `import '@leavittsoftware/web/leavitt/email-history-viewer/email-history-viewer-filled.js'`
1150
+
1151
+ | Kind | Name | Type / values | Notes |
1152
+ | -------- | ----------------------------- | ------------- | ------------------- |
1153
+ | Property | `isActive` | `boolean` | |
1154
+ | Property | `.apiService` | `ApiService` | **Required** |
1155
+ | Property | `path`, `apiControllerName` | `string` | |
1156
+ | Property | `.siteSearchTextFieldContext` | Lit context | Shared search field |
1157
+
1158
+ **Uses:** `titanium-page-control`, filter dialogs, `leavitt-view-sent-email-dialog`
1159
+
1160
+ ---
1161
+
1162
+ ## Profile and user feedback
1163
+
1164
+ ### `profile-picture`
1165
+
1166
+ **Purpose:** CDN-hosted profile image with optional link and test-user indicator.
1167
+
1168
+ **Import:** `import '@leavittsoftware/web/leavitt/profile-picture/profile-picture.js'`
1169
+
1170
+ | Kind | Name | Type / values | Notes |
1171
+ | -------- | ---------------------------------------------------------------- | -------------------- | -------------- |
1172
+ | Property | `fileName` | `string` | CDN filename |
1173
+ | Property | `shape` | `circle` \| `square` | |
1174
+ | Property | `size` | `number` | Pixels |
1175
+ | Property | `show-ring`, `show-test-user-indicator`, `useIntrinsicImageSize` | `boolean` | |
1176
+ | Property | `profile-picture-link-person-id` | `number` | Directory link |
1177
+
1178
+ **CSS parts:** `test-user-indicator`
1179
+
1180
+ ---
1181
+
1182
+ ### `profile-picture-menu`
1183
+
1184
+ **Purpose:** User avatar + account menu popover.
1185
+
1186
+ **Import:** `import '@leavittsoftware/web/leavitt/profile-picture/profile-picture-menu.js'`
1187
+
1188
+ | Kind | Name | Type / values | Notes |
1189
+ | -------- | ------------------------------------------------------------------------ | ----------------------- | ----- |
1190
+ | Property | `.userManager` | `AuthZeroLgUserManager` | |
1191
+ | Property | `size`, `profilePictureFileName`, `personId`, `email`, `company`, `name` | | |
1192
+ | Property | `positioning` | `popover` \| `fixed` | |
1193
+
1194
+ **Slots:** `content`
1195
+
1196
+ **Usage notes / gotchas:** Auto-syncs from `userManager.onIdentityUpdated`; opens auth if no `personId`
1197
+
1198
+ ---
1199
+
1200
+ ### `titanium-profile-picture-stack`
1201
+
1202
+ **Purpose:** Overlapping profile picture stack with overflow count.
1203
+
1204
+ **Import:** `import '@leavittsoftware/web/titanium/profile-picture-stack/profile-picture-stack.js'`
1205
+
1206
+ | Kind | Name | Type / values | Notes |
1207
+ | -------- | -------------------------------------------------------- | ------------------- | ----------- |
1208
+ | Property | `people` | `Partial<Person>[]` | |
1209
+ | Property | `max` | `number` | Max visible |
1210
+ | Property | `size`, `overlap` | `number` | |
1211
+ | Property | `enable-directory-href`, `show-full-name`, `auto-resize` | `boolean` | |
1212
+
1213
+ **CSS parts:** `additional-users`, `additional-users-paragraph`, `name`, `profile-picture`
1214
+
1215
+ **Uses:** `profile-picture` internally
1216
+
1217
+ ---
1218
+
1219
+ ## Service worker notifier
1220
+
1221
+ ### `leavitt-service-worker-notifier`
1222
+
1223
+ **Purpose:** Themed full-screen popover prompting reload after SW update.
1224
+
1225
+ **Import:** `import '@leavittsoftware/web/leavitt/service-worker-notifier/service-worker-notifier.js'`
1226
+
1227
+ **Usage notes / gotchas:** Extends `ThemePreference`; auto-registers SW update listeners; click anywhere reloads
1228
+
1229
+ ---
1230
+
1231
+ # Shared utilities
1232
+
1233
+ ## Events
1234
+
1235
+ | Symbol | Import path | Event name | Payload / usage |
1236
+ | ------------------------------ | ------------------------------------------------------ | ----------------------------------- | ------------------------------------------------------------------------- |
1237
+ | `ShowSnackbarEvent` | `titanium/snackbar/show-snackbar-event` | `show-snackbar` | `message: string \| Partial<HttpError>`, optional `SnackbarOptions` |
1238
+ | `PendingStateEvent` | `titanium/types/pending-state-event` | `pending-state` | `{ promise: Promise<unknown> }`; bubbles, composed |
1239
+ | `DateRangeChangedEvent` | `titanium/date-range-selector/date-range-change-event` | `date-range-changed` | Class exists; `titanium-date-range-selector` fires `change` instead |
1240
+ | `ThemePreferenceEvent` | `leavitt/theme/theme-preference-event` | theme preference changes | Subscribe for dark/light switches |
1241
+ | `DataTableItemsReorderedEvent` | `titanium/data-table/data-table-core` | `titanium-data-table-items-reorder` | Exported from data-table-core; core dispatches `items-reordered` on apply |
1242
+
1243
+ ## Services
1244
+
1245
+ ### `ApiService`
1246
+
1247
+ **Import:** `leavitt/api-service/api-service`
1248
+
1249
+ | Method | Notes |
1250
+ | ------------------------------------------------- | -------------------------- |
1251
+ | `getAsync<T>(urlPath)` | Returns `ODataResponse<T>` |
1252
+ | `postAsync<T>(urlPath, body?)` | JSON POST |
1253
+ | `putAsync`, `patchAsync`, `deleteAsync` | Standard verbs |
1254
+ | `uploadFile(urlPath, file, onprogress, options?)` | XHR upload with progress |
1255
+ | `aggregateResponses(promises)` | Batch multiple async ops |
1256
+ | `addHeader(key, value)` | e.g. `X-LGAppName` |
1257
+
1258
+ **Response helpers:** `ODataResponse.toList()`, `.odataCount`
1259
+
1260
+ **Related:** `HttpError`, `BearerTokenProvider`, `objectToFormData`, `BlobResponse`
1261
+
1262
+ ### `AuthZeroLgUserManager`
1263
+
1264
+ **Import:** `leavitt/user-manager/auth-zero-lg-user-manager`
1265
+
1266
+ Auth0 integration for `profile-picture-menu`, feedback dialogs. Provides `identity`, `authenticate()`, `onIdentityUpdated`.
1267
+
1268
+ ## Controllers and buses
1269
+
1270
+ | Symbol | Import path | Purpose |
1271
+ | --------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------ |
1272
+ | `FilterController` | `titanium/data-table/filter-controller` | URL query-string filter state for list pages |
1273
+ | `TitaniumSiteSearchTextFieldController` | `titanium/site-search-text-field-controller/site-search-text-field-controller` | App-level shared search field via Lit context |
1274
+ | `EventBus` | `titanium/event-bus/event-bus` | Typed pub/sub (`subscribe`, `dispatch`, `unsubscribe`) |
1275
+ | `fileExplorerEvents` | `leavitt/file-explorer/events/file-explorer-events` | File explorer modal sync bus |
1276
+
1277
+ ## Contexts
1278
+
1279
+ | Context | Import path |
1280
+ | -------------------------------- | ------------------------------------------------------------------------------ |
1281
+ | `mainMenuPositionContext` | `leavitt/app/contexts/main-menu-position-context` |
1282
+ | `TitaniumTextFieldSearchContext` | `titanium/site-search-text-field-controller/site-search-text-field-controller` |
1283
+
1284
+ ## Mixins and decorators
1285
+
1286
+ | Symbol | Import path | Purpose |
1287
+ | --------------------- | ---------------------------------------- | ------------------------------------------------------------ |
1288
+ | `promiseTracking` | `titanium/helpers/promise-tracking` | `@promiseTracking('methodName')` decorator for loading flags |
1289
+ | `PendingStateCatcher` | `titanium/helpers/pending-state-catcher` | Mixin to catch pending-state on host |
1290
+ | `ThemePreference` | `leavitt/theme/theme-preference` | Dark/light theme mixin |
1291
+
1292
+ These apply to the **host class** (`LitElement` or `Md*` subclass). They stack with Material Web properties on the same element when the component extends an `Md*` class, or apply to the wrapper when the component composes inner `md-*` children (see **Material Web foundation**).
1293
+
1294
+ ## Helpers (selected)
1295
+
1296
+ | Symbol | Import path | Purpose |
1297
+ | ----------------------------------------------- | --------------------------------------------------------------- | -------------------------- |
1298
+ | `Debouncer` | `titanium/helpers/debouncer` | Debounced async calls |
1299
+ | `delay` | `titanium/helpers/delay` | Promise delay |
1300
+ | `getCdnDownloadUrl`, `getCdnInlineUrl` | `titanium/helpers/get-cdn-download-url`, `get-cdn-Inline-url` | CDN attachment URLs |
1301
+ | `getCompanyMarkUrl`, `getCompanyLogoUrl` | `titanium/helpers/get-company-mark-url`, `get-company-logo-url` | Company branding URLs |
1302
+ | `formatAddress`, address utils | `titanium/helpers/address/*` | Address formatting |
1303
+ | Phone formatters | `titanium/helpers/phone-numbers/*` | Phone number display |
1304
+ | `convertArrayToCsv`, `startCsvDownload` | `titanium/helpers/csv/*` | CSV export |
1305
+ | `getSearchTokens` | `titanium/helpers/get-search-token` | OData search token parsing |
1306
+ | `escapeTerm` | `titanium/helpers/escape-term` | OData string escaping |
1307
+ | `groupBy`, `join`, `middleEllipsis` | `titanium/helpers/*` | General utilities |
1308
+ | `notNull`, `notUndefined`, `notNullOrUndefined` | `titanium/helpers/*` | Type guards |
1309
+ | `installMediaQueryWatcher` | `titanium/helpers/install-media-query-watcher` | Responsive layout callback |
1310
+ | `findScrollableParent` | `titanium/helpers/find-scrollable-parent` | Scroll container detection |
1311
+ | `isDevelopment` | `titanium/helpers/is-development` | Dev environment detection |
1312
+
1313
+ ## Hacks
1314
+
1315
+ | Module | Import path | When to use |
1316
+ | --------------------------- | ----------------------------------------- | ------------------------------------------------------- |
1317
+ | `dialogCloseNavigationHack` | `titanium/hacks/dialog-navigation-hack` | Close dialog on SPA navigation without breaking history |
1318
+ | `dialogZindexHack` | `titanium/hacks/dialog-zindex-hack` | Stacking context issues with nested dialogs |
1319
+ | `dialogOverflowHacks` | `titanium/hacks/dialog-overflow-hacks` | Body scroll lock with dialogs |
1320
+ | `reportValidityIfError` | `titanium/hacks/report-validity-if-error` | Form validation helper |