@propbinder/mobile-design 0.2.50 → 0.2.52

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 (221) hide show
  1. package/fesm2022/propbinder-mobile-design.mjs +26206 -0
  2. package/fesm2022/propbinder-mobile-design.mjs.map +1 -0
  3. package/index.d.ts +8193 -0
  4. package/package.json +39 -3
  5. package/ng-package.json +0 -24
  6. package/src/animations/page-transitions.ts +0 -165
  7. package/src/components/action-list-item/ds-mobile-action-list-item.ts +0 -102
  8. package/src/components/action-list-item/index.ts +0 -2
  9. package/src/components/app-icon/ds-app-icon.ts +0 -133
  10. package/src/components/app-icon/index.ts +0 -2
  11. package/src/components/attachment-preview/ds-mobile-attachment-preview.css +0 -139
  12. package/src/components/attachment-preview/ds-mobile-attachment-preview.ts +0 -164
  13. package/src/components/attachment-preview/index.ts +0 -1
  14. package/src/components/avatar-with-badge/ds-avatar-with-badge.ts +0 -142
  15. package/src/components/avatar-with-badge/index.ts +0 -2
  16. package/src/components/booking-modal/ds-mobile-booking-confirmation-wrapper.ts +0 -71
  17. package/src/components/booking-modal/ds-mobile-booking-modal.service.ts +0 -121
  18. package/src/components/booking-modal/ds-mobile-booking-modal.ts +0 -598
  19. package/src/components/booking-modal/ds-mobile-booking-summary.ts +0 -161
  20. package/src/components/booking-modal/index.ts +0 -4
  21. package/src/components/bottom-sheet/ds-mobile-actions-bottom-sheet.ts +0 -266
  22. package/src/components/bottom-sheet/ds-mobile-bottom-sheet-header.ts +0 -146
  23. package/src/components/bottom-sheet/ds-mobile-bottom-sheet-wrapper.ts +0 -156
  24. package/src/components/bottom-sheet/ds-mobile-bottom-sheet.css +0 -101
  25. package/src/components/bottom-sheet/ds-mobile-bottom-sheet.service.ts +0 -169
  26. package/src/components/bottom-sheet/ds-mobile-confirmation-sheet.ts +0 -211
  27. package/src/components/bottom-sheet/ds-mobile-post-create-bottom-sheet.ts +0 -578
  28. package/src/components/bottom-sheet/ds-mobile-profile-actions-sheet.ts +0 -614
  29. package/src/components/bottom-sheet/index.ts +0 -8
  30. package/src/components/bottom-sheet/modal-shadow-fix.ts +0 -42
  31. package/src/components/card-inline/ds-mobile-card-inline.ts +0 -301
  32. package/src/components/card-inline/index.ts +0 -2
  33. package/src/components/card-inline-banner/ds-mobile-card-inline-banner.ts +0 -118
  34. package/src/components/card-inline-banner/index.ts +0 -1
  35. package/src/components/card-inline-contact/ds-mobile-card-inline-contact.ts +0 -120
  36. package/src/components/card-inline-contact/index.ts +0 -1
  37. package/src/components/card-inline-file/ds-mobile-card-inline-file.ts +0 -141
  38. package/src/components/card-inline-file/index.ts +0 -1
  39. package/src/components/chat-modal/ds-mobile-chat-modal.css +0 -159
  40. package/src/components/chat-modal/ds-mobile-chat-modal.service.ts +0 -105
  41. package/src/components/chat-modal/ds-mobile-chat-modal.ts +0 -918
  42. package/src/components/chat-modal/index.ts +0 -8
  43. package/src/components/comment/ds-mobile-comment.ts +0 -568
  44. package/src/components/comment/index.ts +0 -2
  45. package/src/components/contact-list-item/ds-mobile-contact-list-item.ts +0 -182
  46. package/src/components/contact-list-item/index.ts +0 -2
  47. package/src/components/content/ds-mobile-content.ts +0 -139
  48. package/src/components/content/index.ts +0 -2
  49. package/src/components/dropdown/ds-mobile-dropdown.css +0 -199
  50. package/src/components/dropdown/ds-mobile-dropdown.ts +0 -340
  51. package/src/components/dropdown/index.ts +0 -2
  52. package/src/components/ds-mobile-tabs.css +0 -407
  53. package/src/components/ds-mobile-tabs.ts +0 -216
  54. package/src/components/empty-state/ds-mobile-empty-state.ts +0 -120
  55. package/src/components/empty-state/index.ts +0 -2
  56. package/src/components/fab/ds-mobile-fab.ts +0 -315
  57. package/src/components/fab/index.ts +0 -1
  58. package/src/components/facility-creation-modal/ds-mobile-facility-creation-confirmation-wrapper.ts +0 -121
  59. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.css +0 -189
  60. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.service.ts +0 -135
  61. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.ts +0 -656
  62. package/src/components/facility-creation-modal/index.ts +0 -9
  63. package/src/components/facility-creation-modal/sheets/ds-mobile-access-sheet.ts +0 -105
  64. package/src/components/facility-creation-modal/sheets/ds-mobile-price-sheet.ts +0 -188
  65. package/src/components/facility-creation-modal/sheets/ds-mobile-when-can-book-sheet.ts +0 -460
  66. package/src/components/facility-creation-modal/sheets/ds-mobile-who-can-book-sheet.ts +0 -134
  67. package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.service.ts +0 -69
  68. package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.ts +0 -379
  69. package/src/components/facility-detail-modal/index.ts +0 -2
  70. package/src/components/file-attachment/ds-mobile-file-attachment.ts +0 -164
  71. package/src/components/file-attachment/index.ts +0 -2
  72. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.css +0 -214
  73. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.service.ts +0 -84
  74. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.ts +0 -424
  75. package/src/components/handbook-detail-modal/index.ts +0 -3
  76. package/src/components/handbook-folder/ds-mobile-handbook-folder-mini.ts +0 -175
  77. package/src/components/handbook-folder/ds-mobile-handbook-folder.ts +0 -533
  78. package/src/components/handbook-folder/index.ts +0 -4
  79. package/src/components/header-content/ds-mobile-header-content.ts +0 -222
  80. package/src/components/header-content/index.ts +0 -2
  81. package/src/components/illustration/ds-mobile-illustration.ts +0 -124
  82. package/src/components/illustration/index.ts +0 -2
  83. package/src/components/index.ts +0 -124
  84. package/src/components/inline-photo/ds-mobile-inline-photo.ts +0 -361
  85. package/src/components/inline-photo/index.ts +0 -1
  86. package/src/components/inline-tabs/ds-mobile-inline-tabs.ts +0 -132
  87. package/src/components/inline-tabs/index.ts +0 -2
  88. package/src/components/interactive-list-item-booking/ds-mobile-interactive-list-item-booking.ts +0 -350
  89. package/src/components/interactive-list-item-booking/index.ts +0 -1
  90. package/src/components/interactive-list-item-inquiry/ds-mobile-interactive-list-item-inquiry.ts +0 -321
  91. package/src/components/interactive-list-item-inquiry/index.ts +0 -2
  92. package/src/components/interactive-list-item-message/ds-mobile-interactive-list-item-message.ts +0 -237
  93. package/src/components/interactive-list-item-message/index.ts +0 -2
  94. package/src/components/interactive-list-item-post/ds-mobile-interactive-list-item-post.ts +0 -549
  95. package/src/components/interactive-list-item-post/ds-mobile-post-pdf-attachment.ts +0 -124
  96. package/src/components/interactive-list-item-post/index.ts +0 -13
  97. package/src/components/lightbox/ds-mobile-lightbox-footer.ts +0 -315
  98. package/src/components/lightbox/ds-mobile-lightbox-header.ts +0 -202
  99. package/src/components/lightbox/ds-mobile-lightbox-image.ts +0 -484
  100. package/src/components/lightbox/ds-mobile-lightbox-pdf.css +0 -377
  101. package/src/components/lightbox/ds-mobile-lightbox-pdf.ts +0 -374
  102. package/src/components/lightbox/ds-mobile-lightbox.css +0 -587
  103. package/src/components/lightbox/ds-mobile-lightbox.service.ts +0 -296
  104. package/src/components/lightbox/ds-mobile-lightbox.ts +0 -529
  105. package/src/components/lightbox/index.ts +0 -22
  106. package/src/components/list-item/ds-mobile-list-item.ts +0 -603
  107. package/src/components/list-item/index.ts +0 -2
  108. package/src/components/list-item-static/ds-mobile-list-item-static.ts +0 -133
  109. package/src/components/list-item-static/index.ts +0 -2
  110. package/src/components/loader-overlay/ds-mobile-loader-overlay.css +0 -49
  111. package/src/components/loader-overlay/ds-mobile-loader-overlay.ts +0 -77
  112. package/src/components/loader-overlay/index.ts +0 -1
  113. package/src/components/logo/ds-logo.ts +0 -95
  114. package/src/components/logo/index.ts +0 -2
  115. package/src/components/message-bubble/ds-mobile-message-bubble.ts +0 -633
  116. package/src/components/message-bubble/index.ts +0 -7
  117. package/src/components/message-composer/ds-mobile-message-composer.ts +0 -1146
  118. package/src/components/message-composer/index.ts +0 -7
  119. package/src/components/modal/ds-mobile-modal.css +0 -163
  120. package/src/components/modal/ds-mobile-modal.service.ts +0 -329
  121. package/src/components/modal/index.ts +0 -8
  122. package/src/components/modal-base/ds-mobile-modal-base.css +0 -378
  123. package/src/components/modal-base/ds-mobile-modal-base.ts +0 -261
  124. package/src/components/modal-base/index.ts +0 -2
  125. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.css +0 -112
  126. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.service.ts +0 -93
  127. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.ts +0 -442
  128. package/src/components/new-inquiry-modal/index.ts +0 -4
  129. package/src/components/offline-banner/ds-mobile-offline-banner.ts +0 -135
  130. package/src/components/offline-banner/index.ts +0 -1
  131. package/src/components/page-details/ds-mobile-page-details.css +0 -83
  132. package/src/components/page-details/ds-mobile-page-details.ts +0 -282
  133. package/src/components/page-details/index.ts +0 -2
  134. package/src/components/page-main/ds-mobile-page-main.css +0 -68
  135. package/src/components/page-main/ds-mobile-page-main.ts +0 -421
  136. package/src/components/page-main/index.ts +0 -2
  137. package/src/components/post-composer/ds-mobile-post-composer.ts +0 -140
  138. package/src/components/post-composer/index.ts +0 -2
  139. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.css +0 -390
  140. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.service.ts +0 -108
  141. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.ts +0 -722
  142. package/src/components/post-detail-modal/index.ts +0 -9
  143. package/src/components/property-banner/ds-mobile-property-banner.ts +0 -95
  144. package/src/components/property-banner/index.ts +0 -2
  145. package/src/components/section/ds-mobile-section.ts +0 -263
  146. package/src/components/section/index.ts +0 -2
  147. package/src/components/shared/directives/index.ts +0 -2
  148. package/src/components/shared/directives/long-press.directive.ts +0 -212
  149. package/src/components/shared/index.ts +0 -3
  150. package/src/components/shared/mobile-modal-base.ts +0 -457
  151. package/src/components/shared/mobile-page-base.ts +0 -204
  152. package/src/components/swiper/ds-mobile-swiper-with-nav.ts +0 -160
  153. package/src/components/swiper/ds-mobile-swiper.ts +0 -327
  154. package/src/components/swiper/index.ts +0 -3
  155. package/src/components/system-message-banner/ds-mobile-system-message-banner.ts +0 -129
  156. package/src/components/system-message-banner/index.ts +0 -2
  157. package/src/components/tab-bar/ds-mobile-tab-bar.css +0 -533
  158. package/src/components/tab-bar/ds-mobile-tab-bar.ts +0 -735
  159. package/src/components/tab-bar/index.ts +0 -2
  160. package/src/components/tabs/ds-mobile-tabs.css +0 -25
  161. package/src/components/tabs/ds-mobile-tabs.ts +0 -89
  162. package/src/components/tabs/index.ts +0 -2
  163. package/src/components/text-input/ds-text-input.ts +0 -287
  164. package/src/components/text-input/index.ts +0 -2
  165. package/src/examples/booking.page.ts +0 -434
  166. package/src/examples/community.page.ts +0 -776
  167. package/src/examples/handbook.page.ts +0 -324
  168. package/src/examples/home.page.ts +0 -347
  169. package/src/examples/index.ts +0 -12
  170. package/src/examples/inquiries.example.ts +0 -273
  171. package/src/examples/inquiry-detail.example.css +0 -189
  172. package/src/examples/inquiry-detail.example.ts +0 -415
  173. package/src/examples/mobile-tabs-example.component.ts +0 -208
  174. package/src/examples/post-create.page.ts +0 -311
  175. package/src/examples/post-detail.page.ts +0 -296
  176. package/src/examples/sign-in.page.ts +0 -291
  177. package/src/examples/whitelabel-demo-modal.component.ts +0 -1094
  178. package/src/examples/whitelabel-demo-modal.service.ts +0 -77
  179. package/src/models/index.ts +0 -7
  180. package/src/models/post.model.ts +0 -41
  181. package/src/pages/community.page.ts +0 -769
  182. package/src/pages/handbook.page.ts +0 -388
  183. package/src/pages/home.page.ts +0 -303
  184. package/src/pages/index.ts +0 -11
  185. package/src/pages/inquiries.example.ts +0 -273
  186. package/src/pages/inquiry-detail.example.css +0 -189
  187. package/src/pages/inquiry-detail.example.ts +0 -415
  188. package/src/pages/mobile-tabs-example.component.ts +0 -179
  189. package/src/pages/post-create.page.ts +0 -311
  190. package/src/pages/post-detail.page.ts +0 -296
  191. package/src/pages/sign-in.page.ts +0 -291
  192. package/src/pages/whitelabel-demo-modal.component.ts +0 -1094
  193. package/src/pages/whitelabel-demo-modal.service.ts +0 -77
  194. package/src/public-api.ts +0 -6
  195. package/src/services/base-modal.service.ts +0 -101
  196. package/src/services/index.ts +0 -11
  197. package/src/services/posts.service.ts +0 -542
  198. package/src/services/tracking-permission.service.ts +0 -88
  199. package/src/services/user.service.ts +0 -60
  200. package/src/services/whitelabel.service.ts +0 -675
  201. package/tsconfig.lib.json +0 -17
  202. package/tsconfig.lib.prod.json +0 -9
  203. package/tsconfig.spec.json +0 -13
  204. /package/{src/assets → assets}/fonts/Brockmann-Bold.otf +0 -0
  205. /package/{src/assets → assets}/fonts/Brockmann-BoldItalic.otf +0 -0
  206. /package/{src/assets → assets}/fonts/Brockmann-Medium.otf +0 -0
  207. /package/{src/assets → assets}/fonts/Brockmann-MediumItalic.otf +0 -0
  208. /package/{src/assets → assets}/fonts/Brockmann-Regular.otf +0 -0
  209. /package/{src/assets → assets}/fonts/Brockmann-RegularItalic.otf +0 -0
  210. /package/{src/assets → assets}/fonts/Brockmann-SemiBold.otf +0 -0
  211. /package/{src/assets → assets}/fonts/Brockmann-SemiBoldItalic.otf +0 -0
  212. /package/{src/assets → assets}/fonts/Brockmann_desktop_license.pdf +0 -0
  213. /package/{src/assets → assets}/fonts/brockmann-medium-webfont.woff2 +0 -0
  214. /package/{src/assets → assets}/fonts/brockmann-mediumitalic-webfont.woff2 +0 -0
  215. /package/{src/assets → assets}/fonts/brockmann-regular-webfont.woff2 +0 -0
  216. /package/{src/assets → assets}/fonts/brockmann-regularitalic-webfont.woff2 +0 -0
  217. /package/{src/assets → assets}/fonts/brockmann-semibold-webfont.woff2 +0 -0
  218. /package/{src/assets → assets}/fonts/brockmann-semibolditalic-webfont.woff2 +0 -0
  219. /package/{src/styles → styles}/ionic.css +0 -0
  220. /package/{src/components/shared → styles}/mobile-common.css +0 -0
  221. /package/{src/components/shared → styles}/mobile-page-base.css +0 -0
@@ -1,407 +0,0 @@
1
- /* ============================================
2
- HOST
3
- ============================================ */
4
-
5
- :host {
6
- display: block;
7
- height: 100%;
8
- }
9
-
10
- /* ============================================
11
- ION-TABS BASE
12
- ============================================ */
13
-
14
- ion-tabs {
15
- height: 100%;
16
- background: var(--color-brand-secondary);
17
- }
18
-
19
- /* ============================================
20
- TAB BAR - MOBILE (BOTTOM)
21
- ============================================ */
22
-
23
- .ds-tab-bar {
24
- --background: var(--color-background-neutral-primary);
25
- /* No delay when appearing - start immediately */
26
- transition: transform 0.20s ease-in-out;
27
- }
28
-
29
- /* Mobile: Fixed position for slide transitions */
30
- ion-tab-bar[slot="bottom"] {
31
- border-top: 1px solid var(--border-color-default);
32
- padding-top: 8px;
33
- /* Web browser: 8px minimum, iOS native/web: safe area calculation */
34
- padding-bottom: max(8px, var(--app-safe-bottom, 0px));
35
- padding-left: 12px;
36
- padding-right: 12px;
37
- }
38
-
39
- @media (max-width: 767px) {
40
- ion-tab-bar[slot="bottom"] {
41
- position: fixed;
42
- bottom: 0;
43
- left: 0;
44
- right: 0;
45
- z-index: 100;
46
- padding-bottom: max(8px, var(--app-safe-bottom, 0px));
47
- }
48
- }
49
-
50
- /* Android gesture navigation needs extra breathing room for labels */
51
- @media (max-width: 767px) {
52
- :host-context(.plt-android) ion-tab-bar[slot="bottom"] {
53
- padding-bottom: max(8px, var(--app-safe-bottom, 0px)) !important;
54
- }
55
- }
56
-
57
- /* Hide tab bar when detail page is active with slide-out animation (Mobile only) */
58
- @media (max-width: 767px) {
59
-
60
- /* Only hide if the detail page is NOT hidden (active) */
61
- ion-tabs:has(ds-mobile-page-details:not(.ion-page-hidden)) .ds-tab-bar {
62
- transform: translateY(100%);
63
- /* Add delay when hiding - wait 650ms so it starts hiding 50ms before page transition ends */
64
- transition: transform 0.3s ease;
65
- }
66
- }
67
-
68
- /* Mobile: hide logo and avatar, show tab buttons in a row */
69
- .ds-tab-bar__logo,
70
- .ds-tab-bar__actions {
71
- display: none;
72
- }
73
-
74
- .ds-tab-bar__tabs {
75
- display: flex;
76
- width: 100%;
77
- justify-content: space-around;
78
- align-items: center;
79
- }
80
-
81
- /* On larger screens or PWA mode, remove the extra Safari toolbar padding */
82
- @media (min-width: 769px) {
83
- ion-tab-bar[slot="bottom"] {
84
- padding-bottom: var(--app-safe-bottom, 0px);
85
- }
86
- }
87
-
88
- @media (display-mode: standalone) {
89
- ion-tab-bar[slot="bottom"] {
90
- padding-bottom: var(--app-safe-bottom, 0px) !important;
91
- }
92
- }
93
-
94
- /* ============================================
95
- TAB BAR - DESKTOP (TOP)
96
- ============================================ */
97
-
98
- @media (min-width: 768px) {
99
- ion-tab-bar[slot="top"] {
100
- --background: var(--color-brand-secondary);
101
- position: relative;
102
- /* NOT absolute - part of layout flow */
103
- display: flex;
104
- align-items: center;
105
- padding: 12px 24px;
106
- height: 64px;
107
- max-width: none;
108
- }
109
-
110
- /* Show logo and avatar on desktop */
111
- .ds-tab-bar__logo {
112
- display: flex;
113
- position: absolute;
114
- left: 24px;
115
- align-items: center;
116
- }
117
-
118
- .ds-tab-bar__actions {
119
- display: flex;
120
- position: absolute;
121
- right: 24px;
122
- align-items: center;
123
- gap: 12px;
124
- }
125
-
126
- .ds-tab-bar__tabs {
127
- display: flex;
128
- gap: 8px;
129
- align-items: center;
130
- max-width: 640px;
131
- width: 100%;
132
- margin: 0 auto;
133
- justify-content: center;
134
- padding-left: var(--content-padding-md);
135
- padding-right: var(--content-padding-md);
136
- }
137
-
138
- .logomark {
139
- height: 28px;
140
- width: auto;
141
- flex-shrink: 0;
142
- }
143
- }
144
-
145
- @media (min-width: 992px) {
146
- .ds-tab-bar__tabs {
147
- max-width: 640px;
148
- padding-left: var(--content-padding-lg);
149
- padding-right: var(--content-padding-lg);
150
- justify-content: center;
151
- }
152
- }
153
-
154
- @media (min-width: 1440px) {
155
- .ds-tab-bar__tabs {
156
- max-width: 640px;
157
- padding-left: var(--content-padding-xl);
158
- padding-right: var(--content-padding-xl);
159
- }
160
- }
161
-
162
- @media (min-width: 1768px) {
163
- .ds-tab-bar__tabs {
164
- max-width: 640px;
165
- padding-left: var(--content-padding-2xl);
166
- padding-right: var(--content-padding-2xl);
167
- }
168
- }
169
-
170
- @media (min-width: 1920px) {
171
- .ds-tab-bar__tabs {
172
- max-width: 640px;
173
- padding-left: var(--content-padding-3xl);
174
- padding-right: var(--content-padding-3xl);
175
- }
176
- }
177
-
178
- /* ============================================
179
- TAB BUTTONS - MOBILE
180
- ============================================ */
181
-
182
- ion-tab-button {
183
- --color: var(--text-color-default-tertiary);
184
- --color-selected: var(--color-accent);
185
- display: flex;
186
- flex-direction: column;
187
- align-items: center;
188
- justify-content: center;
189
- position: relative;
190
- overflow: visible;
191
- }
192
-
193
- /* Custom ripple effect - positioned in center of tab button */
194
- .tab-icon-ripple {
195
- position: absolute;
196
- left: 50%;
197
- top: 50%;
198
- width: 40px;
199
- height: 40px;
200
- border-radius: 50%;
201
- background: var(--color-accent);
202
- transform: translate(-50%, -50%) scale(0);
203
- opacity: 0;
204
- pointer-events: none;
205
- transition: all 0.6s cubic-bezier(0.36, 1.2, 0.04, 1.4);
206
- z-index: 0;
207
- }
208
-
209
- /* Trigger ripple on tab selection */
210
- .tab-selected .tab-icon-ripple {
211
- transform: translate(-50%, -50%) scale(2);
212
- opacity: 0.05;
213
- animation: ripple-fade 0.6s cubic-bezier(0.36, 0.5, 0.04, 1.8) forwards;
214
- }
215
-
216
- @keyframes ripple-fade {
217
- 0% {
218
- opacity: 0;
219
- transform: translate(-50%, -50%) scale(0);
220
- }
221
-
222
- 30% {
223
- opacity: 0.1;
224
- }
225
-
226
- 100% {
227
- opacity: 0;
228
- transform: translate(-50%, -50%) scale(2);
229
- }
230
- }
231
-
232
- /* Allow ripple to expand beyond button */
233
- ion-tab-button::part(native) {
234
- overflow: visible;
235
- }
236
-
237
- ion-tab-button ion-ripple-effect {
238
- color: var(--color-accent);
239
- border-radius: 1000px;
240
- }
241
-
242
- ion-tab-button ion-label {
243
- font-size: var(--font-size-xs);
244
- font-weight: 500;
245
- letter-spacing: -0.3px;
246
- margin-top: 0;
247
- }
248
-
249
-
250
- /* ============================================
251
- TAB ICON ANIMATION
252
- ============================================ */
253
-
254
- /* Icon wrapper - relative positioning context */
255
- .tab-icon-wrapper {
256
- position: relative;
257
- width: 24px;
258
- height: 24px;
259
- display: flex;
260
- align-items: center;
261
- justify-content: center;
262
- z-index: 1;
263
- /* Above ripple */
264
- margin-bottom: 4px;
265
- }
266
-
267
- /* Both icons positioned absolutely and centered */
268
- .tab-icon-inactive,
269
- .tab-icon-active {
270
- position: absolute;
271
- left: 50%;
272
- top: 50%;
273
- transform: translate(-50%, -50%);
274
- transition: all 0.8s cubic-bezier(0.36, 1, 0.04, 1);
275
- }
276
-
277
- /* Start state (inactive tab) */
278
- .tab-icon-inactive {
279
- opacity: 1;
280
- transform: translate(-50%, -50%) scale(1);
281
- }
282
-
283
- .tab-icon-active {
284
- opacity: 0;
285
- transform: translate(-50%, calc(-50% - 12px)) scale(0.5);
286
- }
287
-
288
- /* End state (active tab) */
289
- .tab-selected .tab-icon-inactive {
290
- opacity: 0;
291
- transform: translate(-50%, -50%) scale(0.5);
292
- }
293
-
294
- .tab-selected .tab-icon-active {
295
- opacity: 1;
296
- transform: translate(-50%, -50%) scale(1);
297
- }
298
-
299
- /* ============================================
300
- TAB BUTTONS - DESKTOP
301
- ============================================ */
302
-
303
- @media (min-width: 768px) {
304
- .ds-tab-button {
305
- flex-direction: row;
306
- height: 40px;
307
- padding: 0px 16px !important;
308
- border-radius: 40px;
309
- transition: all 0.2s ease;
310
- width: -moz-fit-content;
311
- width: fit-content;
312
- min-width: -moz-fit-content;
313
- min-width: fit-content;
314
- flex: 0 0 auto;
315
- --color: rgba(var(--color-secondary-content-rgb, 255, 255, 255), 0.7);
316
- --color-selected: var(--color-secondary-content, white);
317
- color: rgba(var(--color-secondary-content-rgb, 255, 255, 255), 0.7);
318
- background: rgba(var(--color-secondary-content-rgb, 255, 255, 255), 0.1);
319
- position: relative;
320
- overflow: hidden;
321
- }
322
-
323
- /* Desktop: smaller icon wrapper */
324
- .tab-icon-wrapper {
325
- width: 20px;
326
- height: 20px;
327
- }
328
-
329
- /* Desktop: smaller ripple */
330
- .tab-icon-ripple {
331
- width: 20px;
332
- height: 20px;
333
- }
334
-
335
- /* Ensure native button respects the rounded shape */
336
- .ds-tab-button::part(native) {
337
- border-radius: 40px;
338
- }
339
-
340
- .ds-tab-button:hover:not(.tab-selected) {
341
- --color: var(--color-secondary-content, white);
342
- --color-selected: var(--color-secondary-content, white);
343
- color: var(--color-secondary-content, white);
344
- }
345
-
346
- .ds-tab-button:hover:not(.tab-selected) ion-label {
347
- color: var(--color-secondary-content, white);
348
- }
349
-
350
- .ds-tab-button:hover:not(.tab-selected) ds-icon {
351
- --icon-color: var(--color-secondary-content, white);
352
- color: var(--color-secondary-content, white);
353
- }
354
-
355
- .ds-tab-button:hover:not(.tab-selected) ds-icon svg {
356
- fill: var(--color-secondary-content, white);
357
- }
358
-
359
- .ds-tab-button.tab-selected {
360
- background: var(--color-accent);
361
- --color-selected: var(--color-on-accent, white);
362
- --color: var(--color-on-accent, white);
363
- color: var(--color-on-accent, white);
364
- }
365
-
366
- .ds-tab-button.tab-selected ion-label {
367
- color: var(--color-on-accent, white);
368
- }
369
-
370
- .ds-tab-button .button-native {
371
- width: auto;
372
- padding: 0;
373
- }
374
-
375
- .ds-tab-button ion-label {
376
- font-size: var(--font-size-sm);
377
- font-weight: 500;
378
- margin: 0;
379
- color: inherit;
380
- }
381
-
382
- .ds-tab-button .tab-icon-wrapper {
383
- margin-right: 4px;
384
- margin-bottom: 0px;
385
- }
386
-
387
- .ds-tab-button ion-ripple-effect {
388
- color: rgba(var(--header-content-color-rgb, 255, 255, 255), 0.3);
389
- border-radius: 1000px;
390
- transform: scale(1.5);
391
- }
392
- }
393
-
394
- /* ============================================
395
- AVATAR STYLING
396
- ============================================ */
397
-
398
- @media (min-width: 768px) {
399
- .ds-tab-bar__actions ds-avatar {
400
- cursor: pointer;
401
- transition: transform 0.2s ease;
402
- }
403
-
404
- .ds-tab-bar__actions ds-avatar:hover {
405
- transform: scale(1.05);
406
- }
407
- }
@@ -1,216 +0,0 @@
1
- import { Component, Input, Output, EventEmitter, signal, OnInit, OnDestroy, AfterViewInit, ElementRef, inject, PLATFORM_ID } from '@angular/core';
2
- import { CommonModule, isPlatformBrowser } from '@angular/common';
3
- import {
4
- IonTabs,
5
- IonTabBar,
6
- IonTabButton,
7
- IonLabel
8
- } from '@ionic/angular/standalone';
9
- import { DsIconComponent, DsAvatarComponent } from '@propbinder/design-system';
10
- import { DsLogoComponent } from './logo/ds-logo';
11
-
12
- export interface TabConfig {
13
- id: string;
14
- label: string;
15
- route: string;
16
- icon: string;
17
- iconActive: string;
18
- }
19
-
20
- /**
21
- * DsMobileTabsComponent
22
- *
23
- * Responsive tab navigation that adapts from mobile to desktop:
24
- * - Mobile (< 768px): Bottom tab bar with icons + labels
25
- * - Desktop (≥ 768px): Top navigation bar with logo, tabs, and avatar
26
- *
27
- * Wraps ion-tabs to maintain native routing functionality while
28
- * providing a responsive navigation experience with branding.
29
- *
30
- * @example
31
- * ```html
32
- * <ds-mobile-tabs
33
- * [tabs]="tabsConfig"
34
- * [avatarInitials]="'JD'"
35
- * (avatarClick)="handleAvatarClick()"
36
- * />
37
- * ```
38
- */
39
- @Component({
40
- selector: 'ds-mobile-tabs',
41
- standalone: true,
42
- imports: [
43
- CommonModule,
44
- IonTabs,
45
- IonTabBar,
46
- IonTabButton,
47
- IonLabel,
48
- DsIconComponent,
49
- DsAvatarComponent,
50
- DsLogoComponent
51
- ],
52
- styleUrls: ['./ds-mobile-tabs.css'],
53
- template: `
54
- <ion-tabs>
55
- <ion-tab-bar
56
- [attr.slot]="isDesktop() ? 'top' : 'bottom'"
57
- class="ds-tab-bar"
58
- [class.ds-tab-bar--desktop]="isDesktop()">
59
-
60
- <!-- Logo (desktop only, full logo in header) -->
61
- <div class="ds-tab-bar__logo">
62
- <ds-logo variant="full" size="lg" />
63
- </div>
64
-
65
- <!-- Tab buttons container -->
66
- <div class="ds-tab-bar__tabs" *ngIf="tabs">
67
- <ion-tab-button
68
- *ngFor="let tab of tabs; trackBy: trackByTabId"
69
- [tab]="tab.id"
70
- [attr.data-icon]="tab.icon"
71
- [attr.data-icon-active]="tab.iconActive"
72
- [attr.aria-label]="tab.label"
73
- class="ds-tab-button ion-activatable"
74
- [class.tab-selected]="isTabActive(tab.id)">
75
- <div class="tab-icon-ripple"></div>
76
- <div class="tab-icon-wrapper">
77
- <ds-icon
78
- [name]="tab.icon"
79
- [size]="isDesktop() ? '20px' : '24px'"
80
- class="tab-icon-inactive"
81
- />
82
- <ds-icon
83
- [name]="tab.iconActive"
84
- [size]="isDesktop() ? '20px' : '24px'"
85
- class="tab-icon-active"
86
- />
87
- </div>
88
- <ion-label [attr.aria-hidden]="true">{{ tab.label }}</ion-label>
89
- </ion-tab-button>
90
- </div>
91
-
92
- <!-- Avatar (desktop only, positioned via CSS) -->
93
- <div class="ds-tab-bar__actions">
94
- <ds-avatar
95
- [initials]="avatarInitials"
96
- [type]="avatarType"
97
- [src]="avatarSrc"
98
- [iconName]="avatarIconName"
99
- (click)="handleAvatarClick()"
100
- style="cursor: pointer;"
101
- />
102
- </div>
103
- </ion-tab-bar>
104
- </ion-tabs>
105
- `
106
- })
107
- export class DsMobileTabsComponent implements OnInit, OnDestroy, AfterViewInit {
108
- private platformId = inject(PLATFORM_ID);
109
- private resizeListener?: () => void;
110
- private mutationObserver?: MutationObserver;
111
-
112
- // Inputs
113
- @Input() tabs: TabConfig[] = [];
114
-
115
- // Avatar inputs
116
- @Input() avatarType: 'initials' | 'photo' | 'icon' = 'initials';
117
- @Input() avatarInitials: string = 'U';
118
- @Input() avatarSrc: string = '';
119
- @Input() avatarIconName: string = 'remixUser3Line';
120
-
121
- // Outputs
122
- @Output() avatarClick = new EventEmitter<void>();
123
-
124
- // Internal state
125
- activeTab = signal<string>('');
126
- isDesktop = signal<boolean>(false);
127
-
128
- constructor(private elementRef: ElementRef) {}
129
-
130
- ngOnInit(): void {
131
- console.log('DsMobileTabsComponent initialized');
132
-
133
- // Only run breakpoint detection in browser
134
- if (isPlatformBrowser(this.platformId)) {
135
- // Initial check
136
- this.checkBreakpoint();
137
-
138
- // Listen for window resize
139
- this.resizeListener = () => this.checkBreakpoint();
140
- window.addEventListener('resize', this.resizeListener);
141
- }
142
- }
143
-
144
- ngAfterViewInit(): void {
145
- // Initial removal
146
- this.removeTitleAttributes();
147
-
148
- // Set up mutation observer to continuously remove title attributes
149
- this.setupTitleRemovalObserver();
150
- }
151
-
152
- ngOnDestroy(): void {
153
- if (isPlatformBrowser(this.platformId) && this.resizeListener) {
154
- window.removeEventListener('resize', this.resizeListener);
155
- }
156
- if (this.mutationObserver) {
157
- this.mutationObserver.disconnect();
158
- }
159
- }
160
-
161
- private setupTitleRemovalObserver(): void {
162
- const config = {
163
- attributes: true,
164
- attributeFilter: ['title'],
165
- subtree: true,
166
- childList: true
167
- };
168
-
169
- this.mutationObserver = new MutationObserver((mutations) => {
170
- mutations.forEach((mutation) => {
171
- if (mutation.type === 'attributes' && mutation.attributeName === 'title') {
172
- const target = mutation.target as HTMLElement;
173
- if (target.tagName === 'ION-TAB-BUTTON' && target.hasAttribute('title')) {
174
- target.removeAttribute('title');
175
- }
176
- }
177
- });
178
- // Also do a sweep after any changes
179
- this.removeTitleAttributes();
180
- });
181
-
182
- this.mutationObserver.observe(this.elementRef.nativeElement, config);
183
- }
184
-
185
- private removeTitleAttributes(): void {
186
- const tabButtons = this.elementRef.nativeElement.querySelectorAll('ion-tab-button');
187
- tabButtons.forEach((button: HTMLElement) => {
188
- if (button.hasAttribute('title')) {
189
- button.removeAttribute('title');
190
- }
191
- // Also remove from the native button inside shadow DOM
192
- const nativeButton = button.shadowRoot?.querySelector('button');
193
- if (nativeButton?.hasAttribute('title')) {
194
- nativeButton.removeAttribute('title');
195
- }
196
- });
197
- }
198
-
199
- private checkBreakpoint(): void {
200
- // 768px matches the @media (min-width: 768px) in CSS
201
- this.isDesktop.set(window.innerWidth >= 768);
202
- }
203
-
204
- trackByTabId(index: number, tab: TabConfig): string {
205
- return tab.id;
206
- }
207
-
208
- isTabActive(tabId: string): boolean {
209
- return this.activeTab() === tabId;
210
- }
211
-
212
- handleAvatarClick(): void {
213
- this.avatarClick.emit();
214
- }
215
- }
216
-