@propbinder/mobile-design 0.2.47 → 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 -26136
  206. package/fesm2022/propbinder-mobile-design.mjs.map +0 -1
  207. package/index.d.ts +0 -8154
  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,160 @@
1
+ import { Component, ContentChild, AfterContentInit, ChangeDetectorRef } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { DsIconButtonComponent } from '@propbinder/design-system';
4
+ import { DsMobileSwiperComponent } from './ds-mobile-swiper';
5
+
6
+ /**
7
+ * DsMobileSwiperWithNavComponent
8
+ *
9
+ * Wrapper component that adds navigation buttons to a ds-mobile-swiper.
10
+ * Automatically handles button states (disabled at start/end) and mobile visibility.
11
+ *
12
+ * Features:
13
+ * - Automatic prev/next navigation buttons
14
+ * - Auto-hide buttons when there's only one slide
15
+ * - Auto-disable buttons at start/end of swiper
16
+ * - Hidden on mobile devices (< 768px)
17
+ * - No pointer-events blocking using display: contents
18
+ * - Self-contained navigation logic
19
+ *
20
+ * Usage:
21
+ * ```html
22
+ * <ds-mobile-swiper-with-nav>
23
+ * <ds-mobile-swiper [slideWidth]="'100%'" [gap]="32">
24
+ * <div class="swiper-slide">Slide 1</div>
25
+ * <div class="swiper-slide">Slide 2</div>
26
+ * </ds-mobile-swiper>
27
+ * </ds-mobile-swiper-with-nav>
28
+ * ```
29
+ */
30
+ @Component({
31
+ selector: 'ds-mobile-swiper-with-nav',
32
+ standalone: true,
33
+ imports: [CommonModule, DsIconButtonComponent],
34
+ styles: [`
35
+ :host {
36
+ display: block;
37
+ }
38
+
39
+ .swiper-nav-wrapper {
40
+ position: relative;
41
+ }
42
+
43
+ /* Use display: contents to avoid blocking pointer events */
44
+ .swiper-nav-buttons {
45
+ display: contents;
46
+ }
47
+
48
+ .swiper-nav-button {
49
+ position: absolute;
50
+ top: 50%;
51
+ transform: translateY(-50%);
52
+ z-index: 10;
53
+ }
54
+
55
+ .swiper-nav-button:first-child {
56
+ left: -48px;
57
+ }
58
+
59
+ .swiper-nav-button:last-child {
60
+ right: -48px;
61
+ }
62
+
63
+ /* Force buttons to be perfectly round */
64
+ ::ng-deep .swiper-nav-button button {
65
+ border-radius: 50% !important;
66
+ width: 48px !important;
67
+ height: 48px !important;
68
+ padding: 0 !important;
69
+ }
70
+
71
+ /* Hide on mobile */
72
+ @media (max-width: 767px) {
73
+ .swiper-nav-button {
74
+ display: none;
75
+ }
76
+ }
77
+ `],
78
+ template: `
79
+ <div class="swiper-nav-wrapper">
80
+ <ng-content></ng-content>
81
+
82
+ @if (shouldShowNavButtons()) {
83
+ <div class="swiper-nav-buttons">
84
+ <ds-icon-button
85
+ class="swiper-nav-button"
86
+ icon="remixArrowLeftSLine"
87
+ variant="ghost"
88
+ size="sm"
89
+ [disabled]="isFirstSlide()"
90
+ (clicked)="slidePrev()"
91
+ aria-label="Previous"
92
+ />
93
+ <ds-icon-button
94
+ class="swiper-nav-button"
95
+ icon="remixArrowRightSLine"
96
+ variant="ghost"
97
+ size="sm"
98
+ [disabled]="isLastSlide()"
99
+ (clicked)="slideNext()"
100
+ aria-label="Next"
101
+ />
102
+ </div>
103
+ }
104
+ </div>
105
+ `
106
+ })
107
+ export class DsMobileSwiperWithNavComponent implements AfterContentInit {
108
+ @ContentChild(DsMobileSwiperComponent) swiper?: DsMobileSwiperComponent;
109
+
110
+ constructor(private cdr: ChangeDetectorRef) {}
111
+
112
+ ngAfterContentInit(): void {
113
+ // Trigger change detection when swiper is initialized
114
+ // This ensures button states are correct on initial render
115
+ if (this.swiper) {
116
+ setTimeout(() => {
117
+ this.cdr.detectChanges();
118
+ }, 200);
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Navigate to previous slide
124
+ */
125
+ slidePrev(): void {
126
+ this.swiper?.slidePrev();
127
+ this.cdr.detectChanges();
128
+ }
129
+
130
+ /**
131
+ * Navigate to next slide
132
+ */
133
+ slideNext(): void {
134
+ this.swiper?.slideNext();
135
+ this.cdr.detectChanges();
136
+ }
137
+
138
+ /**
139
+ * Check if at first slide
140
+ */
141
+ isFirstSlide(): boolean {
142
+ return this.swiper?.isBeginning() ?? true;
143
+ }
144
+
145
+ /**
146
+ * Check if at last slide
147
+ */
148
+ isLastSlide(): boolean {
149
+ return this.swiper?.isEnd() ?? true;
150
+ }
151
+
152
+ /**
153
+ * Check if navigation buttons should be shown
154
+ * Hide buttons if there's only one slide
155
+ */
156
+ shouldShowNavButtons(): boolean {
157
+ const slideCount = this.swiper?.getSlideCount() ?? 0;
158
+ return slideCount > 1;
159
+ }
160
+ }
@@ -0,0 +1,327 @@
1
+ import {
2
+ Component,
3
+ input,
4
+ AfterViewInit,
5
+ OnDestroy,
6
+ ElementRef,
7
+ ViewChild,
8
+ CUSTOM_ELEMENTS_SCHEMA
9
+ } from '@angular/core';
10
+ import { CommonModule } from '@angular/common';
11
+ import Swiper from 'swiper';
12
+ import { Pagination } from 'swiper/modules';
13
+
14
+ /**
15
+ * DsMobileSwiperComponent
16
+ *
17
+ * A reusable swiper/carousel component with configurable child width and spacing.
18
+ *
19
+ * Features:
20
+ * - First slide is left-aligned
21
+ * - Middle slides are centered when active
22
+ * - Last slide is right-aligned
23
+ * - Configurable slide width and gap
24
+ * - Content projection via ng-content
25
+ *
26
+ * Usage:
27
+ * ```html
28
+ * <ds-mobile-swiper [slideWidth]="'75vw'" [gap]="16">
29
+ * <div class="swiper-slide">Slide 1</div>
30
+ * <div class="swiper-slide">Slide 2</div>
31
+ * <div class="swiper-slide">Slide 3</div>
32
+ * </ds-mobile-swiper>
33
+ * ```
34
+ */
35
+ @Component({
36
+ selector: 'ds-mobile-swiper',
37
+ standalone: true,
38
+ imports: [CommonModule],
39
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
40
+ template: `
41
+ <div class="swiper-container" #swiperContainer>
42
+ <div class="swiper-wrapper">
43
+ <ng-content></ng-content>
44
+ </div>
45
+ @if (pagination()) {
46
+ <div class="swiper-pagination"></div>
47
+ }
48
+ </div>
49
+ `,
50
+ styles: [`
51
+ :host {
52
+ display: block;
53
+ width: 100%;
54
+ }
55
+
56
+ .swiper-container {
57
+ width: 100%;
58
+ position: relative;
59
+ overflow: visible;
60
+ }
61
+
62
+ .swiper-wrapper {
63
+ display: flex;
64
+ box-sizing: content-box;
65
+ }
66
+
67
+ :host ::ng-deep .swiper-slide {
68
+ flex-shrink: 0;
69
+ height: auto;
70
+ position: relative;
71
+ display: flex;
72
+ align-items: flex-start;
73
+ justify-content: center;
74
+ transition: opacity 300ms ease, transform 300ms ease;
75
+ }
76
+
77
+ /* Hide all slides by default when progressive opacity is enabled */
78
+ :host(.progressive-opacity) ::ng-deep .swiper-slide {
79
+ opacity: 0;
80
+ }
81
+
82
+ /* Show first slide immediately */
83
+ :host(.progressive-opacity) ::ng-deep .swiper-slide:first-child {
84
+ opacity: 1;
85
+ }
86
+
87
+ /* Pagination styles */
88
+ .swiper-pagination {
89
+ position: relative;
90
+ text-align: center;
91
+ display: flex;
92
+ justify-content: center;
93
+ margin-top: 20px;
94
+ margin-bottom: -16px;
95
+ }
96
+
97
+ :host ::ng-deep .swiper-pagination-bullet {
98
+ width: 6px;
99
+ height: 6px;
100
+ border-radius: 50%;
101
+ background: var(--color-accent);
102
+ opacity: 0.25;
103
+ transition: all 0.3s ease;
104
+ cursor: pointer;
105
+ }
106
+
107
+ :host ::ng-deep .swiper-pagination-bullet-active {
108
+ opacity: 1;
109
+ background: var(--color-accent);
110
+ width: 20px;
111
+ border-radius: 3px;
112
+ }
113
+ `]
114
+ })
115
+ export class DsMobileSwiperComponent implements AfterViewInit, OnDestroy {
116
+ /**
117
+ * Width of each slide (e.g., '75vw', '300px', '80%')
118
+ */
119
+ slideWidth = input<string>('75vw');
120
+
121
+ /**
122
+ * Gap between slides in pixels
123
+ */
124
+ gap = input<number>(16);
125
+
126
+ /**
127
+ * Enable pagination dots
128
+ */
129
+ pagination = input<boolean>(false);
130
+
131
+ /**
132
+ * Enable auto height - container adapts to active slide's height
133
+ */
134
+ autoHeight = input<boolean>(false);
135
+
136
+ /**
137
+ * Enable progressive opacity based on slide position
138
+ * Slides fade in/out smoothly as they move toward/away from center
139
+ */
140
+ progressiveOpacity = input<boolean>(false);
141
+
142
+ /**
143
+ * Enable progressive scale based on slide position
144
+ * Slides scale down smoothly as they move away from center
145
+ */
146
+ progressiveScale = input<boolean>(false);
147
+
148
+ @ViewChild('swiperContainer', { static: false }) swiperContainer!: ElementRef;
149
+
150
+ private swiperInstance: Swiper | null = null;
151
+
152
+ constructor(private elementRef: ElementRef) {}
153
+
154
+ ngAfterViewInit(): void {
155
+ // Add progressive-opacity class to host if enabled
156
+ if (this.progressiveOpacity()) {
157
+ this.elementRef.nativeElement.classList.add('progressive-opacity');
158
+ }
159
+
160
+ setTimeout(() => {
161
+ this.initializeSwiper();
162
+ }, 100);
163
+ }
164
+
165
+ private initializeSwiper(): void {
166
+ if (!this.swiperContainer) return;
167
+
168
+ // Apply slide width to all slides
169
+ const slides = this.swiperContainer.nativeElement.querySelectorAll('.swiper-slide');
170
+ slides.forEach((slide: HTMLElement, index: number) => {
171
+ slide.style.width = this.slideWidth();
172
+
173
+ // Set initial opacity BEFORE Swiper initializes to prevent flash of inactive slides
174
+ if (this.progressiveOpacity()) {
175
+ // Hide all slides except the first one (active slide)
176
+ slide.style.opacity = index === 0 ? '1' : '0';
177
+ }
178
+ });
179
+
180
+ const config: any = {
181
+ slidesPerView: 'auto',
182
+ spaceBetween: this.gap(),
183
+ centeredSlides: true,
184
+ centeredSlidesBounds: true,
185
+ speed: 300,
186
+ resistance: true,
187
+ resistanceRatio: 0.85,
188
+ autoHeight: this.autoHeight(),
189
+ watchSlidesProgress: this.progressiveOpacity() || this.progressiveScale(), // Enable progress tracking
190
+ };
191
+
192
+ // Configure event handlers
193
+ config.on = {};
194
+
195
+ // Configure autoHeight animation
196
+ if (this.autoHeight()) {
197
+ config.autoHeight = true;
198
+ // The height transition will use the speed value (300ms)
199
+ config.on.slideChangeTransitionStart = () => {
200
+ // Height transition happens automatically
201
+ };
202
+ }
203
+
204
+ // Configure progressive effects (opacity and/or scale)
205
+ if (this.progressiveOpacity() || this.progressiveScale()) {
206
+ config.on.setTranslate = () => {
207
+ if (!this.swiperInstance) return;
208
+
209
+ this.swiperInstance.slides.forEach((slideEl: any) => {
210
+ const progress = slideEl.progress || 0;
211
+ const absProgress = Math.abs(progress);
212
+
213
+ // Progressive opacity with sharper cutoff
214
+ if (this.progressiveOpacity()) {
215
+ // Make opacity drop off more aggressively
216
+ // Slides with absProgress > 0.5 will be completely hidden
217
+ let opacity;
218
+ if (absProgress > 0.5) {
219
+ opacity = 0;
220
+ } else {
221
+ opacity = 1 - (absProgress * 2); // 2x multiplier for faster fade
222
+ }
223
+ slideEl.style.opacity = Math.max(opacity, 0).toString();
224
+ }
225
+
226
+ // Progressive scale
227
+ if (this.progressiveScale()) {
228
+ // Scale from 1 (center) to 0.9 (edges)
229
+ const minScale = 0.9;
230
+ const scale = 1 - (absProgress * (1 - minScale));
231
+ slideEl.style.transform = `scale(${Math.max(scale, minScale)})`;
232
+ }
233
+ });
234
+ };
235
+
236
+ // Also update on init
237
+ config.on.init = () => {
238
+ if (!this.swiperInstance) return;
239
+
240
+ this.swiperInstance.slides.forEach((slideEl: any) => {
241
+ const progress = slideEl.progress || 0;
242
+ const absProgress = Math.abs(progress);
243
+
244
+ // Set initial opacity with sharper cutoff
245
+ if (this.progressiveOpacity()) {
246
+ let opacity;
247
+ if (absProgress > 0.5) {
248
+ opacity = 0;
249
+ } else {
250
+ opacity = 1 - (absProgress * 2);
251
+ }
252
+ slideEl.style.opacity = Math.max(opacity, 0).toString();
253
+ }
254
+
255
+ // Set initial scale
256
+ if (this.progressiveScale()) {
257
+ const minScale = 0.9;
258
+ const scale = 1 - (absProgress * (1 - minScale));
259
+ slideEl.style.transform = `scale(${Math.max(scale, minScale)})`;
260
+ }
261
+ });
262
+ };
263
+ }
264
+
265
+ // Add pagination if enabled
266
+ if (this.pagination()) {
267
+ config.modules = [Pagination];
268
+ config.pagination = {
269
+ el: '.swiper-pagination',
270
+ clickable: true,
271
+ dynamicBullets: false,
272
+ };
273
+ }
274
+
275
+ this.swiperInstance = new Swiper(this.swiperContainer.nativeElement, config);
276
+ }
277
+
278
+ /**
279
+ * Navigate to previous slide
280
+ */
281
+ slidePrev(): void {
282
+ this.swiperInstance?.slidePrev();
283
+ }
284
+
285
+ /**
286
+ * Navigate to next slide
287
+ */
288
+ slideNext(): void {
289
+ this.swiperInstance?.slideNext();
290
+ }
291
+
292
+ /**
293
+ * Navigate to a specific slide by index
294
+ */
295
+ slideTo(index: number, speed = 300): void {
296
+ this.swiperInstance?.slideTo(index, speed);
297
+ }
298
+
299
+ /**
300
+ * Check if at the beginning
301
+ */
302
+ isBeginning(): boolean {
303
+ return this.swiperInstance?.isBeginning ?? true;
304
+ }
305
+
306
+ /**
307
+ * Check if at the end
308
+ */
309
+ isEnd(): boolean {
310
+ return this.swiperInstance?.isEnd ?? true;
311
+ }
312
+
313
+ /**
314
+ * Get the total number of slides
315
+ */
316
+ getSlideCount(): number {
317
+ return this.swiperInstance?.slides?.length ?? 0;
318
+ }
319
+
320
+ ngOnDestroy(): void {
321
+ if (this.swiperInstance) {
322
+ this.swiperInstance.destroy();
323
+ this.swiperInstance = null;
324
+ }
325
+ }
326
+ }
327
+
@@ -0,0 +1,3 @@
1
+ export * from './ds-mobile-swiper';
2
+ export * from './ds-mobile-swiper-with-nav';
3
+
@@ -0,0 +1,129 @@
1
+ import { Component, input } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ /**
5
+ * DsMobileSystemMessageBannerComponent
6
+ *
7
+ * Full-width centered banner component for displaying system messages in chat conversations.
8
+ * Uses the same text styling as message bubbles for consistency.
9
+ *
10
+ * Features:
11
+ * - Full-width centered layout
12
+ * - Subtle background with theming support
13
+ * - Same typography as message bubbles
14
+ * - Optional icon support
15
+ *
16
+ * Common use cases:
17
+ * - Inquiry status updates ("Your inquiry has been assigned to...")
18
+ * - System notifications ("This inquiry is marked as resolved")
19
+ * - Auto-replies ("We aim to respond within 24 hours...")
20
+ * - Time/date separators
21
+ *
22
+ * @example
23
+ * ```html
24
+ * <!-- Simple system message -->
25
+ * <ds-mobile-system-message-banner
26
+ * [message]="'Ricki Meihlen har overtaget din henvendelse og vil kontakte dig snart.'">
27
+ * </ds-mobile-system-message-banner>
28
+ *
29
+ * <!-- With icon -->
30
+ * <ds-mobile-system-message-banner
31
+ * [message]="'Vi bestræber os på at svare inden for 24 timer på hverdage.'"
32
+ * [iconName]="'remixInformationLine'">
33
+ * </ds-mobile-system-message-banner>
34
+ * ```
35
+ */
36
+ @Component({
37
+ selector: 'ds-mobile-system-message-banner',
38
+ standalone: true,
39
+ imports: [CommonModule],
40
+ host: {
41
+ '[class.after-timestamp]': 'afterTimestamp()'
42
+ },
43
+ styles: [`
44
+ :host {
45
+ display: block;
46
+ width: 100%;
47
+ padding: 12px 0;
48
+ }
49
+
50
+ :host(.after-timestamp) {
51
+ padding-top: 0;
52
+ }
53
+
54
+ .system-message-container {
55
+ display: flex;
56
+ justify-content: center;
57
+ align-items: center;
58
+ width: 100%;
59
+ padding: 0 16px;
60
+ }
61
+
62
+ .system-message-content {
63
+ display: flex;
64
+ align-items: center;
65
+ justify-content: center;
66
+ gap: 8px;
67
+ padding: 8px 16px;
68
+ background: var(--color-background-neutral-secondary, #f5f5f5);
69
+ border-radius: 16px;
70
+ max-width: 85%;
71
+ text-align: center;
72
+ }
73
+
74
+ .system-message-icon {
75
+ flex-shrink: 0;
76
+ width: 16px;
77
+ height: 16px;
78
+ color: var(--color-text-tertiary, #a0a0a0);
79
+ }
80
+
81
+ .system-message-text {
82
+ font-family: 'Brockmann', sans-serif;
83
+ font-size: var(--font-size-sm);
84
+ font-weight: 400;
85
+ line-height: 20px;
86
+ letter-spacing: -0.3px;
87
+ color: var(--color-text-secondary, #666666);
88
+ margin: 0;
89
+ }
90
+ `],
91
+ template: `
92
+ <div class="system-message-container">
93
+ <div class="system-message-content">
94
+ @if (iconName()) {
95
+ <span class="system-message-icon">
96
+ <!-- Icon slot - you can add ds-icon component here if needed -->
97
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
98
+ <path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM11 7h2v2h-2V7zm0 4h2v6h-2v-6z"/>
99
+ </svg>
100
+ </span>
101
+ }
102
+ <p class="system-message-text">{{ message() }}</p>
103
+ </div>
104
+ </div>
105
+ `
106
+ })
107
+ export class DsMobileSystemMessageBannerComponent {
108
+ /**
109
+ * System message text to display
110
+ */
111
+ message = input.required<string>();
112
+
113
+ /**
114
+ * Optional icon name (currently using inline SVG for info icon)
115
+ * Can be extended to support full icon library integration
116
+ */
117
+ iconName = input<string>('');
118
+
119
+ /**
120
+ * Whether this system message appears directly after a timestamp
121
+ * When true, removes top padding to reduce spacing
122
+ */
123
+ afterTimestamp = input<boolean>(false);
124
+ }
125
+
126
+
127
+
128
+
129
+
@@ -0,0 +1,2 @@
1
+ export * from './ds-mobile-system-message-banner';
2
+