@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,9 @@
1
+ /**
2
+ * Mobile Post Detail Modal Module
3
+ *
4
+ * Service and component for displaying posts in a modal
5
+ */
6
+
7
+ export * from './ds-mobile-post-detail-modal';
8
+ export * from './ds-mobile-post-detail-modal.service';
9
+
@@ -0,0 +1,95 @@
1
+ import { Component, input } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ /**
5
+ * DsMobilePropertyBannerComponent
6
+ *
7
+ * Compact banner displaying property photo and address.
8
+ * Designed for use in page headers to show current property context.
9
+ *
10
+ * @example
11
+ * ```html
12
+ * <ds-mobile-property-banner
13
+ * address="Toftegårds Allé 5A, 2. tv."
14
+ * photoUrl="/Assets/building.jpg">
15
+ * </ds-mobile-property-banner>
16
+ * ```
17
+ */
18
+ @Component({
19
+ selector: 'ds-mobile-property-banner',
20
+ standalone: true,
21
+ imports: [CommonModule],
22
+ styles: [`
23
+ :host {
24
+ display: block;
25
+ width: 100%;
26
+ }
27
+
28
+ .property-banner {
29
+ display: flex;
30
+ align-items: center;
31
+ gap: 8px;
32
+ height: 40px;
33
+ padding: 0 8px;
34
+ background: rgba(var(--header-content-color-rgb, 255, 255, 255), 0.1);
35
+ border: none;
36
+ backdrop-filter: blur(10px);
37
+ border-radius: 12px;
38
+ cursor: pointer;
39
+ transition: background 0.2s ease;
40
+ -webkit-tap-highlight-color: transparent;
41
+ }
42
+
43
+ .property-banner:hover {
44
+ background: rgba(var(--header-content-color-rgb, 255, 255, 255), 0.12);
45
+ }
46
+
47
+ .property-banner:active {
48
+ background: rgba(var(--header-content-color-rgb, 255, 255, 255), 0.15);
49
+ }
50
+
51
+ .property-photo {
52
+ width: 24px;
53
+ height: 24px;
54
+ border-radius: 6px;
55
+ object-fit: cover;
56
+ flex-shrink: 0;
57
+ background: rgba(var(--header-content-color-rgb, 255, 255, 255), 0.1);
58
+ }
59
+
60
+ .property-address {
61
+ font-family: 'Brockmann', sans-serif;
62
+ font-size: var(--font-size-sm);
63
+ font-weight: 500;
64
+ color: var(--header-content-color, white);
65
+ line-height: 1.4;
66
+ flex: 1;
67
+ min-width: 0;
68
+ white-space: nowrap;
69
+ overflow: hidden;
70
+ text-overflow: ellipsis;
71
+ }
72
+ `],
73
+ template: `
74
+ <div class="property-banner">
75
+ <img
76
+ [src]="photoUrl()"
77
+ [alt]="address()"
78
+ class="property-photo"
79
+ />
80
+ <span class="property-address">{{ address() }}</span>
81
+ </div>
82
+ `
83
+ })
84
+ export class DsMobilePropertyBannerComponent {
85
+ /**
86
+ * Property address text
87
+ */
88
+ address = input.required<string>();
89
+
90
+ /**
91
+ * URL to property photo
92
+ */
93
+ photoUrl = input.required<string>();
94
+ }
95
+
@@ -0,0 +1,2 @@
1
+ export * from './ds-mobile-property-banner';
2
+
@@ -0,0 +1,263 @@
1
+ import { Component, input, output } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { DsIconComponent } from '@propbinder/design-system';
4
+
5
+ /**
6
+ * DsMobileSectionComponent
7
+ *
8
+ * Universal section component for mobile pages, modals, and content containers.
9
+ * Provides consistent layout with optional headlines, action links, flexible padding, and borders.
10
+ *
11
+ * **Features:**
12
+ * - Optional section headline with icon support
13
+ * - Optional action link with click handler
14
+ * - Flexible padding control
15
+ * - Border management (bottom border by default)
16
+ * - Works in pages, modals, and any content container
17
+ *
18
+ * **Usage Patterns:**
19
+ *
20
+ * @example
21
+ * ```html
22
+ * <!-- Section with headline and link -->
23
+ * <ds-mobile-section
24
+ * headline="Recent Posts"
25
+ * linkText="See all"
26
+ * (linkClick)="navigate()">
27
+ * <div class="posts-list">...</div>
28
+ * </ds-mobile-section>
29
+ *
30
+ * <!-- Section with icon in headline -->
31
+ * <ds-mobile-section
32
+ * headline="Messages"
33
+ * icon="remixChat3Line"
34
+ * linkText="View all"
35
+ * (linkClick)="viewMessages()">
36
+ * <div class="messages">...</div>
37
+ * </ds-mobile-section>
38
+ *
39
+ * <!-- Empty state section (no headline) -->
40
+ * <ds-mobile-section>
41
+ * <div class="empty-state">
42
+ * <ds-avatar type="icon" iconName="remixInboxLine" />
43
+ * <h3>No messages yet</h3>
44
+ * </div>
45
+ * </ds-mobile-section>
46
+ *
47
+ * <!-- Last section without border -->
48
+ * <ds-mobile-section
49
+ * headline="Contact"
50
+ * [showBorder]="false">
51
+ * <div class="contact-form">...</div>
52
+ * </ds-mobile-section>
53
+ *
54
+ * <!-- Section with custom padding -->
55
+ * <ds-mobile-section
56
+ * headline="Photos"
57
+ * padding="20px 0">
58
+ * <div class="photo-grid">...</div>
59
+ * </ds-mobile-section>
60
+ *
61
+ * <!-- Full-width section in page (default behavior) -->
62
+ * <ds-mobile-page-main title="Home">
63
+ * <ds-mobile-section headline="Posts">
64
+ * <div class="posts">...</div>
65
+ * </ds-mobile-section>
66
+ * </ds-mobile-page-main>
67
+ * ```
68
+ */
69
+ @Component({
70
+ selector: 'ds-mobile-section',
71
+ standalone: true,
72
+ imports: [CommonModule, DsIconComponent],
73
+ host: {
74
+ '[class.has-border]': 'showBorder()'
75
+ },
76
+ styles: [`
77
+ :host {
78
+ display: block;
79
+ width: 100%;
80
+ border-bottom: 1px solid transparent;
81
+ transition: border-color 0.2s ease;
82
+ }
83
+
84
+ :host(.has-border) {
85
+ border-bottom-color: var(--border-color-default);
86
+ }
87
+
88
+ /* Automatically hide border on last section */
89
+ :host(.has-border):last-child {
90
+ border-bottom-color: transparent;
91
+ }
92
+
93
+ .section {
94
+ width: 100%;
95
+ display: flex;
96
+ flex-direction: column;
97
+ padding: var(--section-padding, 20px);
98
+ gap: var(--section-gap, 12px);
99
+ overflow: var(--section-overflow);
100
+ }
101
+
102
+ /* Increase padding on desktop breakpoints */
103
+ @media (min-width: 768px) {
104
+ .section {
105
+ padding: var(--section-padding, 32px);
106
+ }
107
+
108
+ /* Remove horizontal padding on desktop when inside page components */
109
+ ds-mobile-page-main :host .section,
110
+ ds-mobile-page-details :host .section {
111
+ padding-left: 0;
112
+ padding-right: 0;
113
+ }
114
+ }
115
+
116
+ .section-header {
117
+ display: flex;
118
+ align-items: center;
119
+ justify-content: space-between;
120
+ gap: 12px;
121
+ min-height: 24px;
122
+ }
123
+
124
+ .section-headline {
125
+ font-family: 'Brockmann', sans-serif;
126
+ font-size: var(--font-size-base);
127
+ font-weight: 600;
128
+ color: var(--text-color-default-primary);
129
+ margin: 0;
130
+ padding: 0;
131
+ letter-spacing: -0.2px;
132
+ display: flex;
133
+ align-items: center;
134
+ gap: 6px;
135
+ line-height: 1.4;
136
+ }
137
+
138
+ .section-link {
139
+ font-family: 'Brockmann', sans-serif;
140
+ font-size: var(--font-size-sm);
141
+ font-weight: 500;
142
+ color: var(--color-accent, #5d5fef);
143
+ text-decoration: none;
144
+ cursor: pointer;
145
+ white-space: nowrap;
146
+ transition: opacity 0.2s ease;
147
+ -webkit-tap-highlight-color: transparent;
148
+ display: flex;
149
+ align-items: center;
150
+ gap: 2px;
151
+ line-height: 1;
152
+ user-select: none;
153
+ }
154
+
155
+ .section-link:hover {
156
+ opacity: 0.8;
157
+ }
158
+
159
+ .section-link:active {
160
+ opacity: 0.6;
161
+ }
162
+
163
+ .section-content {
164
+ display: flex;
165
+ flex-direction: column;
166
+ gap: var(--section-content-gap);
167
+ }
168
+ `],
169
+ template: `
170
+ <section class="section" [style.--section-padding]="padding()" [style.--section-gap]="gap()" [style.--section-content-gap]="contentGap()" [style.--section-overflow]="overflow()">
171
+ @if (headline() || linkText()) {
172
+ <header class="section-header">
173
+ @if (headline()) {
174
+ <h2 class="section-headline">
175
+ @if (icon()) {
176
+ <ds-icon [name]="icon()!" size="20px" />
177
+ }
178
+ {{ headline() }}
179
+ </h2>
180
+ }
181
+ @if (linkText()) {
182
+ <a class="section-link" (click)="handleLinkClick()" [attr.role]="'button'" [attr.tabindex]="0">
183
+ {{ linkText() }}
184
+ <ds-icon name="remixArrowRightSLine" size="16px" />
185
+ </a>
186
+ }
187
+ <ng-content select="[header-action]" />
188
+ </header>
189
+ }
190
+
191
+ <div class="section-content">
192
+ <ng-content />
193
+ </div>
194
+ </section>
195
+ `
196
+ })
197
+ export class DsMobileSectionComponent {
198
+ /**
199
+ * Section headline text
200
+ * @default ''
201
+ */
202
+ headline = input<string>('');
203
+
204
+ /**
205
+ * Optional icon to display before headline
206
+ * @default ''
207
+ */
208
+ icon = input<string>('');
209
+
210
+ /**
211
+ * Link text (e.g., "See all", "View more")
212
+ * When provided, displays a clickable link in the section header
213
+ * @default ''
214
+ */
215
+ linkText = input<string>('');
216
+
217
+ /**
218
+ * Section padding
219
+ * Accepts any valid CSS padding value
220
+ * When not set, defaults to 20px on mobile and 32px on desktop
221
+ * @default ''
222
+ */
223
+ padding = input<string>('');
224
+
225
+ /**
226
+ * Gap between section header and content
227
+ * Accepts any valid CSS gap value
228
+ * @default '12px'
229
+ */
230
+ gap = input<string>('12px');
231
+
232
+ /**
233
+ * Gap between child elements within section-content
234
+ * Accepts any valid CSS gap value
235
+ * @default '12px'
236
+ */
237
+ contentGap = input<string>('12px');
238
+
239
+ /**
240
+ * Whether to show bottom border
241
+ * @default true
242
+ */
243
+ showBorder = input<boolean>(true);
244
+
245
+ /**
246
+ * CSS overflow property for the section
247
+ * @default 'visible'
248
+ */
249
+ overflow = input<string>('visible');
250
+
251
+ /**
252
+ * Emitted when section link is clicked
253
+ */
254
+ linkClick = output<void>();
255
+
256
+ /**
257
+ * Handle link click event
258
+ */
259
+ handleLinkClick(): void {
260
+ this.linkClick.emit();
261
+ }
262
+ }
263
+
@@ -0,0 +1,2 @@
1
+ export { DsMobileSectionComponent } from './ds-mobile-section';
2
+
@@ -0,0 +1,2 @@
1
+ export * from './long-press.directive';
2
+
@@ -0,0 +1,212 @@
1
+ import {
2
+ Directive,
3
+ Output,
4
+ EventEmitter,
5
+ HostListener,
6
+ Input,
7
+ OnDestroy
8
+ } from '@angular/core';
9
+ import { Haptics, ImpactStyle } from '@capacitor/haptics';
10
+
11
+ /**
12
+ * DsMobileLongPressDirective
13
+ *
14
+ * A reusable directive for handling long press interactions on mobile devices.
15
+ * Provides haptic feedback and prevents long press when touching interactive elements.
16
+ *
17
+ * Features:
18
+ * - Configurable duration and movement threshold
19
+ * - Automatic haptic feedback (with fallback to navigator.vibrate)
20
+ * - Excludes interactive elements (buttons, links, inputs)
21
+ * - Handles touchmove cancellation
22
+ * - Context menu support (right-click on desktop)
23
+ *
24
+ * @example
25
+ * ```html
26
+ * <!-- Basic usage -->
27
+ * <div dsMobileLongPress (longPress)="handleLongPress()">
28
+ * Long press me
29
+ * </div>
30
+ *
31
+ * <!-- Custom duration and threshold -->
32
+ * <div
33
+ * dsMobileLongPress
34
+ * [longPressDuration]="800"
35
+ * [moveThreshold]="15"
36
+ * [excludeSelectors]="'button, a, .no-longpress'"
37
+ * (longPress)="showContextMenu()">
38
+ * Custom long press
39
+ * </div>
40
+ * ```
41
+ */
42
+ @Directive({
43
+ selector: '[dsMobileLongPress]',
44
+ standalone: true
45
+ })
46
+ export class DsMobileLongPressDirective implements OnDestroy {
47
+ /**
48
+ * Duration in milliseconds to trigger long press
49
+ * @default 500
50
+ */
51
+ @Input() longPressDuration = 500;
52
+
53
+ /**
54
+ * Maximum movement in pixels before canceling long press
55
+ * @default 10
56
+ */
57
+ @Input() moveThreshold = 10;
58
+
59
+ /**
60
+ * CSS selectors to exclude from long press detection
61
+ * @default 'button, a, input, select, textarea, [role="button"]'
62
+ */
63
+ @Input() excludeSelectors = 'button, a, input, select, textarea, [role="button"]';
64
+
65
+ /**
66
+ * Haptic feedback style (Light, Medium, Heavy)
67
+ * @default ImpactStyle.Medium
68
+ */
69
+ @Input() hapticStyle: ImpactStyle = ImpactStyle.Medium;
70
+
71
+ /**
72
+ * Enable/disable haptic feedback
73
+ * @default true
74
+ */
75
+ @Input() enableHaptics = true;
76
+
77
+ /**
78
+ * Emits when long press is triggered
79
+ */
80
+ @Output() longPress = new EventEmitter<void>();
81
+
82
+ /**
83
+ * Emits when long press starts (timer begins)
84
+ */
85
+ @Output() longPressStart = new EventEmitter<void>();
86
+
87
+ /**
88
+ * Emits when long press is cancelled
89
+ */
90
+ @Output() longPressCancel = new EventEmitter<void>();
91
+
92
+ private longPressTimer: any = null;
93
+ private longPressTriggered = false;
94
+ private touchStartX = 0;
95
+ private touchStartY = 0;
96
+
97
+ /**
98
+ * Handle touch start for long press detection
99
+ */
100
+ @HostListener('touchstart', ['$event'])
101
+ handleTouchStart(event: TouchEvent): void {
102
+ // Don't start long press if touching interactive child elements
103
+ // But allow if the closest match is the host element itself (where the directive is attached)
104
+ const target = event.target as HTMLElement;
105
+ const closestExcluded = target.closest(this.excludeSelectors);
106
+ const hostElement = (event.currentTarget as HTMLElement);
107
+
108
+ // Only exclude if we found an excluded element AND it's not the host itself
109
+ if (closestExcluded && closestExcluded !== hostElement) {
110
+ return;
111
+ }
112
+
113
+ this.longPressTriggered = false;
114
+ this.touchStartX = event.touches[0].clientX;
115
+ this.touchStartY = event.touches[0].clientY;
116
+
117
+ // Emit start event
118
+ this.longPressStart.emit();
119
+
120
+ // Start long press timer
121
+ this.longPressTimer = setTimeout(async () => {
122
+ this.longPressTriggered = true;
123
+ this.longPress.emit();
124
+
125
+ // Haptic feedback for long press
126
+ if (this.enableHaptics) {
127
+ await this.triggerHaptics();
128
+ }
129
+ }, this.longPressDuration);
130
+ }
131
+
132
+ /**
133
+ * Handle touch end to clear long press timer
134
+ */
135
+ @HostListener('touchend', ['$event'])
136
+ handleTouchEnd(event: TouchEvent): void {
137
+ if (this.longPressTimer) {
138
+ clearTimeout(this.longPressTimer);
139
+ this.longPressTimer = null;
140
+
141
+ if (!this.longPressTriggered) {
142
+ this.longPressCancel.emit();
143
+ }
144
+ }
145
+
146
+ // Prevent normal click if long press was triggered
147
+ if (this.longPressTriggered) {
148
+ event.preventDefault();
149
+ event.stopPropagation();
150
+ this.longPressTriggered = false;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Handle touch move to cancel long press if moved too much
156
+ */
157
+ @HostListener('touchmove', ['$event'])
158
+ handleTouchMove(event: TouchEvent): void {
159
+ if (!this.longPressTimer) return;
160
+
161
+ const touch = event.touches[0];
162
+ const deltaX = Math.abs(touch.clientX - this.touchStartX);
163
+ const deltaY = Math.abs(touch.clientY - this.touchStartY);
164
+
165
+ // Cancel long press if moved too far
166
+ if (deltaX > this.moveThreshold || deltaY > this.moveThreshold) {
167
+ clearTimeout(this.longPressTimer);
168
+ this.longPressTimer = null;
169
+ this.longPressTriggered = false;
170
+ this.longPressCancel.emit();
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Handle context menu (right-click on desktop) to trigger long press action
176
+ */
177
+ @HostListener('contextmenu', ['$event'])
178
+ handleContextMenu(event: Event): void {
179
+ event.preventDefault();
180
+ this.longPress.emit();
181
+ }
182
+
183
+ /**
184
+ * Trigger haptic feedback
185
+ */
186
+ private async triggerHaptics(): Promise<void> {
187
+ try {
188
+ await Haptics.impact({ style: this.hapticStyle });
189
+ } catch {
190
+ // Fallback to Web Vibration API if Capacitor Haptics is not available
191
+ if ('vibrate' in navigator) {
192
+ // Map haptic styles to vibration durations
193
+ const vibrationMap = {
194
+ [ImpactStyle.Light]: 30,
195
+ [ImpactStyle.Medium]: 50,
196
+ [ImpactStyle.Heavy]: 80
197
+ };
198
+ navigator.vibrate(vibrationMap[this.hapticStyle] || 50);
199
+ }
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Cleanup on destroy
205
+ */
206
+ ngOnDestroy(): void {
207
+ if (this.longPressTimer) {
208
+ clearTimeout(this.longPressTimer);
209
+ this.longPressTimer = null;
210
+ }
211
+ }
212
+ }
@@ -0,0 +1,3 @@
1
+ export * from './mobile-page-base';
2
+ export * from './directives';
3
+