@propbinder/mobile-design 0.2.48 → 0.2.50

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/ng-package.json +24 -0
  2. package/package.json +3 -39
  3. package/src/animations/page-transitions.ts +165 -0
  4. package/src/assets/fonts/brockmann-mediumitalic-webfont.woff2 +0 -0
  5. package/src/assets/fonts/brockmann-regularitalic-webfont.woff2 +0 -0
  6. package/src/assets/fonts/brockmann-semibolditalic-webfont.woff2 +0 -0
  7. package/src/components/action-list-item/ds-mobile-action-list-item.ts +102 -0
  8. package/src/components/action-list-item/index.ts +2 -0
  9. package/src/components/app-icon/ds-app-icon.ts +133 -0
  10. package/src/components/app-icon/index.ts +2 -0
  11. package/src/components/attachment-preview/ds-mobile-attachment-preview.css +139 -0
  12. package/src/components/attachment-preview/ds-mobile-attachment-preview.ts +164 -0
  13. package/src/components/attachment-preview/index.ts +1 -0
  14. package/src/components/avatar-with-badge/ds-avatar-with-badge.ts +142 -0
  15. package/src/components/avatar-with-badge/index.ts +2 -0
  16. package/src/components/booking-modal/ds-mobile-booking-confirmation-wrapper.ts +71 -0
  17. package/src/components/booking-modal/ds-mobile-booking-modal.service.ts +121 -0
  18. package/src/components/booking-modal/ds-mobile-booking-modal.ts +598 -0
  19. package/src/components/booking-modal/ds-mobile-booking-summary.ts +161 -0
  20. package/src/components/booking-modal/index.ts +4 -0
  21. package/src/components/bottom-sheet/ds-mobile-actions-bottom-sheet.ts +266 -0
  22. package/src/components/bottom-sheet/ds-mobile-bottom-sheet-header.ts +146 -0
  23. package/src/components/bottom-sheet/ds-mobile-bottom-sheet-wrapper.ts +156 -0
  24. package/src/components/bottom-sheet/ds-mobile-bottom-sheet.css +101 -0
  25. package/src/components/bottom-sheet/ds-mobile-bottom-sheet.service.ts +169 -0
  26. package/src/components/bottom-sheet/ds-mobile-confirmation-sheet.ts +211 -0
  27. package/src/components/bottom-sheet/ds-mobile-post-create-bottom-sheet.ts +578 -0
  28. package/src/components/bottom-sheet/ds-mobile-profile-actions-sheet.ts +614 -0
  29. package/src/components/bottom-sheet/index.ts +8 -0
  30. package/src/components/bottom-sheet/modal-shadow-fix.ts +42 -0
  31. package/src/components/card-inline/ds-mobile-card-inline.ts +301 -0
  32. package/src/components/card-inline/index.ts +2 -0
  33. package/src/components/card-inline-banner/ds-mobile-card-inline-banner.ts +118 -0
  34. package/src/components/card-inline-banner/index.ts +1 -0
  35. package/src/components/card-inline-contact/ds-mobile-card-inline-contact.ts +120 -0
  36. package/src/components/card-inline-contact/index.ts +1 -0
  37. package/src/components/card-inline-file/ds-mobile-card-inline-file.ts +141 -0
  38. package/src/components/card-inline-file/index.ts +1 -0
  39. package/src/components/chat-modal/ds-mobile-chat-modal.css +159 -0
  40. package/src/components/chat-modal/ds-mobile-chat-modal.service.ts +105 -0
  41. package/src/components/chat-modal/ds-mobile-chat-modal.ts +918 -0
  42. package/src/components/chat-modal/index.ts +8 -0
  43. package/src/components/comment/ds-mobile-comment.ts +568 -0
  44. package/src/components/comment/index.ts +2 -0
  45. package/src/components/contact-list-item/ds-mobile-contact-list-item.ts +182 -0
  46. package/src/components/contact-list-item/index.ts +2 -0
  47. package/src/components/content/ds-mobile-content.ts +139 -0
  48. package/src/components/content/index.ts +2 -0
  49. package/src/components/dropdown/ds-mobile-dropdown.css +199 -0
  50. package/src/components/dropdown/ds-mobile-dropdown.ts +340 -0
  51. package/src/components/dropdown/index.ts +2 -0
  52. package/src/components/ds-mobile-tabs.css +407 -0
  53. package/src/components/ds-mobile-tabs.ts +216 -0
  54. package/src/components/empty-state/ds-mobile-empty-state.ts +120 -0
  55. package/src/components/empty-state/index.ts +2 -0
  56. package/src/components/fab/ds-mobile-fab.ts +315 -0
  57. package/src/components/fab/index.ts +1 -0
  58. package/src/components/facility-creation-modal/ds-mobile-facility-creation-confirmation-wrapper.ts +121 -0
  59. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.css +189 -0
  60. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.service.ts +135 -0
  61. package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.ts +656 -0
  62. package/src/components/facility-creation-modal/index.ts +9 -0
  63. package/src/components/facility-creation-modal/sheets/ds-mobile-access-sheet.ts +105 -0
  64. package/src/components/facility-creation-modal/sheets/ds-mobile-price-sheet.ts +188 -0
  65. package/src/components/facility-creation-modal/sheets/ds-mobile-when-can-book-sheet.ts +460 -0
  66. package/src/components/facility-creation-modal/sheets/ds-mobile-who-can-book-sheet.ts +134 -0
  67. package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.service.ts +69 -0
  68. package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.ts +379 -0
  69. package/src/components/facility-detail-modal/index.ts +2 -0
  70. package/src/components/file-attachment/ds-mobile-file-attachment.ts +164 -0
  71. package/src/components/file-attachment/index.ts +2 -0
  72. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.css +214 -0
  73. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.service.ts +84 -0
  74. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.ts +424 -0
  75. package/src/components/handbook-detail-modal/index.ts +3 -0
  76. package/src/components/handbook-folder/ds-mobile-handbook-folder-mini.ts +175 -0
  77. package/src/components/handbook-folder/ds-mobile-handbook-folder.ts +533 -0
  78. package/src/components/handbook-folder/index.ts +4 -0
  79. package/src/components/header-content/ds-mobile-header-content.ts +222 -0
  80. package/src/components/header-content/index.ts +2 -0
  81. package/src/components/illustration/ds-mobile-illustration.ts +124 -0
  82. package/src/components/illustration/index.ts +2 -0
  83. package/src/components/index.ts +124 -0
  84. package/src/components/inline-photo/ds-mobile-inline-photo.ts +361 -0
  85. package/src/components/inline-photo/index.ts +1 -0
  86. package/src/components/inline-tabs/ds-mobile-inline-tabs.ts +132 -0
  87. package/src/components/inline-tabs/index.ts +2 -0
  88. package/src/components/interactive-list-item-booking/ds-mobile-interactive-list-item-booking.ts +350 -0
  89. package/src/components/interactive-list-item-booking/index.ts +1 -0
  90. package/src/components/interactive-list-item-inquiry/ds-mobile-interactive-list-item-inquiry.ts +321 -0
  91. package/src/components/interactive-list-item-inquiry/index.ts +2 -0
  92. package/src/components/interactive-list-item-message/ds-mobile-interactive-list-item-message.ts +237 -0
  93. package/src/components/interactive-list-item-message/index.ts +2 -0
  94. package/src/components/interactive-list-item-post/ds-mobile-interactive-list-item-post.ts +549 -0
  95. package/src/components/interactive-list-item-post/ds-mobile-post-pdf-attachment.ts +124 -0
  96. package/src/components/interactive-list-item-post/index.ts +13 -0
  97. package/src/components/lightbox/ds-mobile-lightbox-footer.ts +315 -0
  98. package/src/components/lightbox/ds-mobile-lightbox-header.ts +202 -0
  99. package/src/components/lightbox/ds-mobile-lightbox-image.ts +484 -0
  100. package/src/components/lightbox/ds-mobile-lightbox-pdf.css +377 -0
  101. package/src/components/lightbox/ds-mobile-lightbox-pdf.ts +374 -0
  102. package/src/components/lightbox/ds-mobile-lightbox.css +587 -0
  103. package/src/components/lightbox/ds-mobile-lightbox.service.ts +296 -0
  104. package/src/components/lightbox/ds-mobile-lightbox.ts +529 -0
  105. package/src/components/lightbox/index.ts +22 -0
  106. package/src/components/list-item/ds-mobile-list-item.ts +603 -0
  107. package/src/components/list-item/index.ts +2 -0
  108. package/src/components/list-item-static/ds-mobile-list-item-static.ts +133 -0
  109. package/src/components/list-item-static/index.ts +2 -0
  110. package/src/components/loader-overlay/ds-mobile-loader-overlay.css +49 -0
  111. package/src/components/loader-overlay/ds-mobile-loader-overlay.ts +77 -0
  112. package/src/components/loader-overlay/index.ts +1 -0
  113. package/src/components/logo/ds-logo.ts +95 -0
  114. package/src/components/logo/index.ts +2 -0
  115. package/src/components/message-bubble/ds-mobile-message-bubble.ts +633 -0
  116. package/src/components/message-bubble/index.ts +7 -0
  117. package/src/components/message-composer/ds-mobile-message-composer.ts +1146 -0
  118. package/src/components/message-composer/index.ts +7 -0
  119. package/src/components/modal/ds-mobile-modal.css +163 -0
  120. package/src/components/modal/ds-mobile-modal.service.ts +329 -0
  121. package/src/components/modal/index.ts +8 -0
  122. package/src/components/modal-base/ds-mobile-modal-base.css +378 -0
  123. package/src/components/modal-base/ds-mobile-modal-base.ts +261 -0
  124. package/src/components/modal-base/index.ts +2 -0
  125. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.css +112 -0
  126. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.service.ts +93 -0
  127. package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.ts +442 -0
  128. package/src/components/new-inquiry-modal/index.ts +4 -0
  129. package/src/components/offline-banner/ds-mobile-offline-banner.ts +135 -0
  130. package/src/components/offline-banner/index.ts +1 -0
  131. package/src/components/page-details/ds-mobile-page-details.css +83 -0
  132. package/src/components/page-details/ds-mobile-page-details.ts +282 -0
  133. package/src/components/page-details/index.ts +2 -0
  134. package/src/components/page-main/ds-mobile-page-main.css +68 -0
  135. package/src/components/page-main/ds-mobile-page-main.ts +421 -0
  136. package/src/components/page-main/index.ts +2 -0
  137. package/src/components/post-composer/ds-mobile-post-composer.ts +140 -0
  138. package/src/components/post-composer/index.ts +2 -0
  139. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.css +390 -0
  140. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.service.ts +108 -0
  141. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.ts +722 -0
  142. package/src/components/post-detail-modal/index.ts +9 -0
  143. package/src/components/property-banner/ds-mobile-property-banner.ts +95 -0
  144. package/src/components/property-banner/index.ts +2 -0
  145. package/src/components/section/ds-mobile-section.ts +263 -0
  146. package/src/components/section/index.ts +2 -0
  147. package/src/components/shared/directives/index.ts +2 -0
  148. package/src/components/shared/directives/long-press.directive.ts +212 -0
  149. package/src/components/shared/index.ts +3 -0
  150. package/src/components/shared/mobile-modal-base.ts +457 -0
  151. package/src/components/shared/mobile-page-base.ts +204 -0
  152. package/src/components/swiper/ds-mobile-swiper-with-nav.ts +160 -0
  153. package/src/components/swiper/ds-mobile-swiper.ts +327 -0
  154. package/src/components/swiper/index.ts +3 -0
  155. package/src/components/system-message-banner/ds-mobile-system-message-banner.ts +129 -0
  156. package/src/components/system-message-banner/index.ts +2 -0
  157. package/src/components/tab-bar/ds-mobile-tab-bar.css +533 -0
  158. package/src/components/tab-bar/ds-mobile-tab-bar.ts +735 -0
  159. package/src/components/tab-bar/index.ts +2 -0
  160. package/src/components/tabs/ds-mobile-tabs.css +25 -0
  161. package/src/components/tabs/ds-mobile-tabs.ts +89 -0
  162. package/src/components/tabs/index.ts +2 -0
  163. package/src/components/text-input/ds-text-input.ts +287 -0
  164. package/src/components/text-input/index.ts +2 -0
  165. package/src/examples/booking.page.ts +434 -0
  166. package/src/examples/community.page.ts +776 -0
  167. package/src/examples/handbook.page.ts +324 -0
  168. package/src/examples/home.page.ts +347 -0
  169. package/src/examples/index.ts +12 -0
  170. package/src/examples/inquiries.example.ts +273 -0
  171. package/src/examples/inquiry-detail.example.css +189 -0
  172. package/src/examples/inquiry-detail.example.ts +415 -0
  173. package/src/examples/mobile-tabs-example.component.ts +208 -0
  174. package/src/examples/post-create.page.ts +311 -0
  175. package/src/examples/post-detail.page.ts +296 -0
  176. package/src/examples/sign-in.page.ts +291 -0
  177. package/src/examples/whitelabel-demo-modal.component.ts +1094 -0
  178. package/src/examples/whitelabel-demo-modal.service.ts +77 -0
  179. package/src/models/index.ts +7 -0
  180. package/src/models/post.model.ts +41 -0
  181. package/src/pages/community.page.ts +769 -0
  182. package/src/pages/handbook.page.ts +388 -0
  183. package/src/pages/home.page.ts +303 -0
  184. package/src/pages/index.ts +11 -0
  185. package/src/pages/inquiries.example.ts +273 -0
  186. package/src/pages/inquiry-detail.example.css +189 -0
  187. package/src/pages/inquiry-detail.example.ts +415 -0
  188. package/src/pages/mobile-tabs-example.component.ts +179 -0
  189. package/src/pages/post-create.page.ts +311 -0
  190. package/src/pages/post-detail.page.ts +296 -0
  191. package/src/pages/sign-in.page.ts +291 -0
  192. package/src/pages/whitelabel-demo-modal.component.ts +1094 -0
  193. package/src/pages/whitelabel-demo-modal.service.ts +77 -0
  194. package/src/public-api.ts +6 -0
  195. package/src/services/base-modal.service.ts +101 -0
  196. package/src/services/index.ts +11 -0
  197. package/src/services/posts.service.ts +542 -0
  198. package/src/services/tracking-permission.service.ts +88 -0
  199. package/src/services/user.service.ts +60 -0
  200. package/src/services/whitelabel.service.ts +675 -0
  201. package/{styles → src/styles}/ionic.css +25 -0
  202. package/tsconfig.lib.json +17 -0
  203. package/tsconfig.lib.prod.json +9 -0
  204. package/tsconfig.spec.json +13 -0
  205. package/fesm2022/propbinder-mobile-design.mjs +0 -26168
  206. package/fesm2022/propbinder-mobile-design.mjs.map +0 -1
  207. package/index.d.ts +0 -8169
  208. /package/{assets → src/assets}/fonts/Brockmann-Bold.otf +0 -0
  209. /package/{assets → src/assets}/fonts/Brockmann-BoldItalic.otf +0 -0
  210. /package/{assets → src/assets}/fonts/Brockmann-Medium.otf +0 -0
  211. /package/{assets → src/assets}/fonts/Brockmann-MediumItalic.otf +0 -0
  212. /package/{assets → src/assets}/fonts/Brockmann-Regular.otf +0 -0
  213. /package/{assets → src/assets}/fonts/Brockmann-RegularItalic.otf +0 -0
  214. /package/{assets → src/assets}/fonts/Brockmann-SemiBold.otf +0 -0
  215. /package/{assets → src/assets}/fonts/Brockmann-SemiBoldItalic.otf +0 -0
  216. /package/{assets → src/assets}/fonts/Brockmann_desktop_license.pdf +0 -0
  217. /package/{assets → src/assets}/fonts/brockmann-medium-webfont.woff2 +0 -0
  218. /package/{assets → src/assets}/fonts/brockmann-regular-webfont.woff2 +0 -0
  219. /package/{assets → src/assets}/fonts/brockmann-semibold-webfont.woff2 +0 -0
  220. /package/{styles → src/components/shared}/mobile-common.css +0 -0
  221. /package/{styles → src/components/shared}/mobile-page-base.css +0 -0
@@ -0,0 +1,361 @@
1
+ import { Component, Input, output, signal, OnInit } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { DsMobileLightboxService, LightboxImage } from '../lightbox/ds-mobile-lightbox.service';
4
+ import { DsMobileLoaderOverlayComponent } from '../loader-overlay';
5
+
6
+ /**
7
+ * DsMobileInlinePhotoComponent
8
+ *
9
+ * Displays one or multiple photos in a grid layout optimized for social feeds.
10
+ * Supports up to 5 visible images with automatic grid layouts.
11
+ *
12
+ * Features:
13
+ * - Automatic grid layouts for 1-5 images
14
+ * - Shows "+N more" overlay if more than 5 images
15
+ * - Opens lightbox with all images (including hidden ones) when clicked
16
+ * - Optimized layouts: 1 full, 2 split, 3 masonry, 4 grid, 5 grid
17
+ *
18
+ * @example
19
+ * ```html
20
+ * <ds-mobile-inline-photo
21
+ * [images]="['img1.jpg', 'img2.jpg', 'img3.jpg']"
22
+ * [author]="authorInfo"
23
+ * />
24
+ * ```
25
+ */
26
+ @Component({
27
+ selector: 'ds-mobile-inline-photo',
28
+ standalone: true,
29
+ imports: [CommonModule, DsMobileLoaderOverlayComponent],
30
+ template: `
31
+ <div
32
+ class="photo-grid"
33
+ [class.flex-layout]="!useGrid"
34
+ [attr.data-count]="useGrid ? visibleImages.length : null"
35
+ [class.has-more]="hiddenCount > 0">
36
+ @for (image of visibleImages; track image; let i = $index) {
37
+ <div
38
+ class="photo-item"
39
+ [class.last]="i === visibleImages.length - 1"
40
+ (click)="openLightbox(i, $event)">
41
+ <img
42
+ [src]="image"
43
+ [alt]="'Photo ' + (i + 1)"
44
+ (load)="onImageLoad(i)"
45
+ (error)="onImageError(i)"
46
+ loading="lazy">
47
+
48
+ <!-- Loading Overlay -->
49
+ @if (isImageLoading(i)) {
50
+ <ds-mobile-loader-overlay />
51
+ }
52
+
53
+ <!-- Show "+N more" overlay on last image if there are hidden images -->
54
+ @if (i === visibleImages.length - 1 && hiddenCount > 0) {
55
+ <div class="more-overlay">
56
+ <span class="more-text">+{{ hiddenCount }}</span>
57
+ </div>
58
+ }
59
+ </div>
60
+ }
61
+ </div>
62
+ `,
63
+ styles: [`
64
+ :host {
65
+ display: block;
66
+ }
67
+
68
+ .photo-grid {
69
+ display: grid;
70
+ gap: 4px;
71
+ border-radius: 8px;
72
+ overflow: hidden;
73
+ background: var(--ds-color-neutral-100);
74
+ width: 100%;
75
+ }
76
+
77
+ .photo-item {
78
+ position: relative;
79
+ overflow: hidden;
80
+ cursor: pointer;
81
+ background: var(--ds-color-neutral-200);
82
+ aspect-ratio: 1;
83
+ }
84
+
85
+ .photo-item img {
86
+ width: 100%;
87
+ height: 100%;
88
+ object-fit: cover;
89
+ display: block;
90
+ transition: transform 0.2s ease;
91
+ }
92
+
93
+ .photo-item:active img {
94
+ transform: scale(0.98);
95
+ }
96
+
97
+ .more-overlay {
98
+ position: absolute;
99
+ inset: 0;
100
+ background: rgba(0, 0, 0, 0.6);
101
+ display: flex;
102
+ align-items: center;
103
+ justify-content: center;
104
+ pointer-events: none;
105
+ }
106
+
107
+ .more-text {
108
+ color: white;
109
+ font-size: 32px;
110
+ font-weight: 600;
111
+ font-family: var(--ds-font-family-medium);
112
+ }
113
+
114
+ /* 1 image: full width, 4:3 ratio */
115
+ .photo-grid[data-count="1"] {
116
+ grid-template-columns: 1fr;
117
+ }
118
+
119
+ .photo-grid[data-count="1"] .photo-item {
120
+ aspect-ratio: 4/3;
121
+ }
122
+
123
+ /* 2 images: side by side */
124
+ .photo-grid[data-count="2"] {
125
+ grid-template-columns: 1fr 1fr;
126
+ }
127
+
128
+ /* 3 images: 1 large left, 2 stacked right */
129
+ .photo-grid[data-count="3"] {
130
+ grid-template-columns: 2fr 1fr;
131
+ grid-auto-rows: 1fr;
132
+ }
133
+
134
+ .photo-grid[data-count="3"] .photo-item:first-child {
135
+ grid-row: 1 / 3;
136
+ aspect-ratio: auto;
137
+ }
138
+
139
+ /* Right side images remain 1:1 squares */
140
+ .photo-grid[data-count="3"] .photo-item:nth-child(2),
141
+ .photo-grid[data-count="3"] .photo-item:nth-child(3) {
142
+ aspect-ratio: 1;
143
+ }
144
+
145
+ /* 4 images: 2x2 grid */
146
+ .photo-grid[data-count="4"] {
147
+ grid-template-columns: 1fr 1fr;
148
+ grid-template-rows: 1fr 1fr;
149
+ }
150
+
151
+ /* 5 images: 2 on top, 3 on bottom */
152
+ .photo-grid[data-count="5"] {
153
+ grid-template-columns: repeat(6, 1fr);
154
+ grid-template-rows: auto auto;
155
+ }
156
+
157
+ .photo-grid[data-count="5"] .photo-item {
158
+ aspect-ratio: 1;
159
+ width: 100%;
160
+ }
161
+
162
+ .photo-grid[data-count="5"] .photo-item:nth-child(1) {
163
+ grid-column: 1 / 4;
164
+ grid-row: 1;
165
+ }
166
+
167
+ .photo-grid[data-count="5"] .photo-item:nth-child(2) {
168
+ grid-column: 4 / 7;
169
+ grid-row: 1;
170
+ }
171
+
172
+ .photo-grid[data-count="5"] .photo-item:nth-child(3) {
173
+ grid-column: 1 / 3;
174
+ grid-row: 2;
175
+ }
176
+
177
+ .photo-grid[data-count="5"] .photo-item:nth-child(4) {
178
+ grid-column: 3 / 5;
179
+ grid-row: 2;
180
+ }
181
+
182
+ .photo-grid[data-count="5"] .photo-item:nth-child(5) {
183
+ grid-column: 5 / 7;
184
+ grid-row: 2;
185
+ }
186
+
187
+ /* Round bottom corners for 5-image layout */
188
+ .photo-grid[data-count="5"] .photo-item:nth-child(3) {
189
+ border-bottom-left-radius: 8px;
190
+ overflow: hidden;
191
+ }
192
+
193
+ .photo-grid[data-count="5"] .photo-item:nth-child(5) {
194
+ border-bottom-right-radius: 8px;
195
+ overflow: hidden;
196
+ }
197
+
198
+ /* ============================================
199
+ FLEX LAYOUT (when useGrid is false)
200
+ ============================================ */
201
+
202
+ .photo-grid.flex-layout {
203
+ display: flex;
204
+ flex-wrap: wrap;
205
+ gap: 8px;
206
+ border-radius: 0;
207
+ background: transparent;
208
+ }
209
+
210
+ .photo-grid.flex-layout .photo-item {
211
+ flex: 0 0 auto;
212
+ width: 96px;
213
+ height: 96px;
214
+ aspect-ratio: 1;
215
+ border-radius: 8px;
216
+ overflow: hidden;
217
+ }
218
+
219
+ .photo-grid.flex-layout .photo-item img {
220
+ border-radius: 8px;
221
+ }
222
+ `]
223
+ })
224
+ export class DsMobileInlinePhotoComponent implements OnInit {
225
+ /**
226
+ * Array of image URLs to display
227
+ */
228
+ @Input() images: string[] = [];
229
+
230
+ /**
231
+ * Optional array of loading states for each image (by index)
232
+ * If provided, shows loader overlay for images that are still loading
233
+ */
234
+ @Input() loadingStates?: boolean[];
235
+
236
+ /**
237
+ * Internal signal to track image loading states
238
+ */
239
+ private internalLoadingStates = signal<Map<number, boolean>>(new Map());
240
+
241
+ /**
242
+ * Author information (passed to lightbox)
243
+ */
244
+ @Input() author?: {
245
+ name: string;
246
+ role?: string;
247
+ avatarSrc?: string;
248
+ avatarInitials?: string;
249
+ avatarType?: 'photo' | 'initials';
250
+ timestamp?: string;
251
+ };
252
+
253
+ /**
254
+ * Maximum number of images to show inline (default: 5)
255
+ * Remaining images shown in lightbox only
256
+ */
257
+ @Input() maxVisible: number = 5;
258
+
259
+ /**
260
+ * Whether to use grid layout (true) or flex-wrap layout (false)
261
+ * @default true
262
+ */
263
+ @Input() useGrid: boolean = true;
264
+
265
+ /**
266
+ * Event emitted when lightbox is opened
267
+ */
268
+ photoClick = output<{ index: number; totalImages: number }>();
269
+
270
+ constructor(private lightboxService: DsMobileLightboxService) {}
271
+
272
+ /**
273
+ * Initialize loading states for all visible images
274
+ */
275
+ ngOnInit(): void {
276
+ // Set all images as loading initially
277
+ const loadingMap = new Map<number, boolean>();
278
+ this.visibleImages.forEach((_, index) => {
279
+ loadingMap.set(index, true);
280
+ });
281
+ this.internalLoadingStates.set(loadingMap);
282
+ }
283
+
284
+ /**
285
+ * Handle image load event
286
+ */
287
+ onImageLoad(index: number): void {
288
+ const loadingMap = new Map(this.internalLoadingStates());
289
+ loadingMap.set(index, false);
290
+ this.internalLoadingStates.set(loadingMap);
291
+ }
292
+
293
+ /**
294
+ * Handle image error event
295
+ */
296
+ onImageError(index: number): void {
297
+ const loadingMap = new Map(this.internalLoadingStates());
298
+ loadingMap.set(index, false);
299
+ this.internalLoadingStates.set(loadingMap);
300
+ }
301
+
302
+ /**
303
+ * Get the first N images to display inline
304
+ */
305
+ get visibleImages(): string[] {
306
+ return this.images.slice(0, this.maxVisible);
307
+ }
308
+
309
+ /**
310
+ * Check if a specific image is loading
311
+ */
312
+ isImageLoading(index: number): boolean {
313
+ // Use external loadingStates if provided, otherwise use internal state
314
+ if (this.loadingStates !== undefined) {
315
+ return this.loadingStates[index] ?? false;
316
+ }
317
+ return this.internalLoadingStates().get(index) ?? true;
318
+ }
319
+
320
+ /**
321
+ * Calculate how many images are hidden
322
+ */
323
+ get hiddenCount(): number {
324
+ return Math.max(0, this.images.length - this.maxVisible);
325
+ }
326
+
327
+ /**
328
+ * Open lightbox with all images, starting at the clicked index
329
+ */
330
+ openLightbox(index: number, event?: Event): void {
331
+ // Stop event propagation to prevent triggering parent click handlers
332
+ if (event) {
333
+ event.stopPropagation();
334
+ event.preventDefault();
335
+ }
336
+
337
+ // Emit event
338
+ this.photoClick.emit({
339
+ index,
340
+ totalImages: this.images.length
341
+ });
342
+
343
+ // Convert image URLs to LightboxImage format
344
+ const lightboxImages: LightboxImage[] = this.images.map((src, i) => ({
345
+ type: 'image',
346
+ src,
347
+ alt: `Photo ${i + 1}`
348
+ }));
349
+
350
+ // Open lightbox with all images (not just visible ones)
351
+ this.lightboxService.openImages({
352
+ images: lightboxImages,
353
+ initialIndex: index,
354
+ author: this.author,
355
+ enableZoom: true,
356
+ showControls: true,
357
+ enableSwipe: true
358
+ });
359
+ }
360
+ }
361
+
@@ -0,0 +1 @@
1
+ export * from './ds-mobile-inline-photo';
@@ -0,0 +1,132 @@
1
+ import { Component, input, output } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ export interface InlineTabItem {
5
+ id: string;
6
+ label: string;
7
+ badge?: number;
8
+ }
9
+
10
+ /**
11
+ * DsMobileInlineTabsComponent
12
+ *
13
+ * Pill-style inline tabs for filtering/switching views
14
+ * Used in the purple header section of pages
15
+ *
16
+ * @example
17
+ * ```html
18
+ * <ds-mobile-inline-tabs
19
+ * [tabs]="[
20
+ * { id: 'all', label: 'All' },
21
+ * { id: 'open', label: 'Open' },
22
+ * { id: 'closed', label: 'Closed' }
23
+ * ]"
24
+ * [activeTab]="'all'"
25
+ * (tabChange)="handleTabChange($event)">
26
+ * </ds-mobile-inline-tabs>
27
+ * ```
28
+ */
29
+ @Component({
30
+ selector: 'ds-mobile-inline-tabs',
31
+ standalone: true,
32
+ imports: [CommonModule],
33
+ styles: [`
34
+ .filter-tabs {
35
+ display: flex;
36
+ align-items: center;
37
+ gap: 8px;
38
+ flex-wrap: wrap;
39
+ height: 40px;
40
+ }
41
+
42
+ .filter-tab {
43
+ min-width: 48px;
44
+ display: flex;
45
+ justify-content: center;
46
+ align-items: center;
47
+ padding: 0px 12px;
48
+ height: 32px;
49
+ border-radius: 20px;
50
+ background: transparent;
51
+ border: none;
52
+ font-family: 'Brockmann', sans-serif;
53
+ font-size: var(--font-size-sm);
54
+ font-weight: 500;
55
+ color: rgba(var(--header-content-color-rgb, 255, 255, 255), 0.6);
56
+ cursor: pointer;
57
+ transition: all 0.2s ease;
58
+ display: flex;
59
+ align-items: center;
60
+ gap: 6px;
61
+ white-space: nowrap;
62
+ }
63
+
64
+ .filter-tab.active {
65
+ background: var(--color-header-accent, #5d5fef);
66
+ color: var(--color-on-header-accent, white);
67
+ }
68
+
69
+ .filter-tab:hover:not(.active) {
70
+ background: rgba(var(--header-content-color-rgb, 255, 255, 255), 0.1);
71
+ }
72
+
73
+ .tab-badge {
74
+ min-width: 24px;
75
+ height: 16px;
76
+ padding: 0 6px;
77
+ border-radius: 10px;
78
+ background: rgba(var(--header-content-color-rgb, 255, 255, 255), 0.2);
79
+ color: var(--header-content-color, white);
80
+ font-family: 'Brockmann', sans-serif;
81
+ font-size: var(--font-size-xs);
82
+ font-weight: 600;
83
+ display: flex;
84
+ align-items: center;
85
+ justify-content: center;
86
+ line-height: 1;
87
+ }
88
+
89
+ .filter-tab.active .tab-badge {
90
+ background: rgba(var(--color-on-header-accent-rgb, 255, 255, 255), 0.3);
91
+ color: var(--color-on-header-accent, white);
92
+ }
93
+ `],
94
+ template: `
95
+ <div class="filter-tabs">
96
+ @for (tab of tabs(); track tab.id) {
97
+ <button
98
+ class="filter-tab"
99
+ [class.active]="activeTab() === tab.id"
100
+ (click)="handleTabClick(tab.id)"
101
+ [attr.aria-label]="tab.label"
102
+ [attr.aria-selected]="activeTab() === tab.id">
103
+ {{ tab.label }}
104
+ @if (tab.badge && tab.badge > 0) {
105
+ <span class="tab-badge">{{ tab.badge }}</span>
106
+ }
107
+ </button>
108
+ }
109
+ </div>
110
+ `
111
+ })
112
+ export class DsMobileInlineTabsComponent {
113
+ /**
114
+ * Array of tab items to display
115
+ */
116
+ tabs = input.required<InlineTabItem[]>();
117
+
118
+ /**
119
+ * Currently active tab ID
120
+ */
121
+ activeTab = input.required<string>();
122
+
123
+ /**
124
+ * Emitted when a tab is clicked
125
+ */
126
+ tabChange = output<string>();
127
+
128
+ handleTabClick(tabId: string): void {
129
+ this.tabChange.emit(tabId);
130
+ }
131
+ }
132
+
@@ -0,0 +1,2 @@
1
+ export { DsMobileInlineTabsComponent, type InlineTabItem } from './ds-mobile-inline-tabs';
2
+