@propbinder/mobile-design 0.0.2 → 0.0.22

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 (123) hide show
  1. package/fesm2022/propbinder-mobile-design.mjs +12604 -0
  2. package/fesm2022/propbinder-mobile-design.mjs.map +1 -0
  3. package/index.d.ts +3214 -0
  4. package/package.json +39 -12
  5. package/ng-package.json +0 -7
  6. package/src/animations/page-transitions.ts +0 -86
  7. package/src/assets/fonts/Brockmann-Bold.otf +0 -0
  8. package/src/assets/fonts/Brockmann-BoldItalic.otf +0 -0
  9. package/src/assets/fonts/Brockmann-Medium.otf +0 -0
  10. package/src/assets/fonts/Brockmann-MediumItalic.otf +0 -0
  11. package/src/assets/fonts/Brockmann-Regular.otf +0 -0
  12. package/src/assets/fonts/Brockmann-RegularItalic.otf +0 -0
  13. package/src/assets/fonts/Brockmann-SemiBold.otf +0 -0
  14. package/src/assets/fonts/Brockmann-SemiBoldItalic.otf +0 -0
  15. package/src/assets/fonts/Brockmann_desktop_license.pdf +0 -0
  16. package/src/assets/fonts/brockmann-medium-webfont.woff2 +0 -0
  17. package/src/assets/fonts/brockmann-regular-webfont.woff2 +0 -0
  18. package/src/assets/fonts/brockmann-semibold-webfont.woff2 +0 -0
  19. package/src/components/action-list-item/ds-mobile-action-list-item.ts +0 -83
  20. package/src/components/action-list-item/index.ts +0 -2
  21. package/src/components/app-layout/ds-mobile-app-layout.css +0 -343
  22. package/src/components/app-layout/ds-mobile-app-layout.ts +0 -271
  23. package/src/components/app-layout/index.ts +0 -2
  24. package/src/components/avatar-with-badge/ds-avatar-with-badge.ts +0 -130
  25. package/src/components/avatar-with-badge/index.ts +0 -2
  26. package/src/components/bottom-sheet/ds-mobile-actions-bottom-sheet.ts +0 -273
  27. package/src/components/bottom-sheet/ds-mobile-bottom-sheet.css +0 -110
  28. package/src/components/bottom-sheet/ds-mobile-bottom-sheet.service.ts +0 -167
  29. package/src/components/bottom-sheet/ds-mobile-post-create-bottom-sheet.ts +0 -656
  30. package/src/components/bottom-sheet/index.ts +0 -3
  31. package/src/components/comment/ds-mobile-comment.ts +0 -516
  32. package/src/components/comment/index.ts +0 -2
  33. package/src/components/contact-list-item/ds-mobile-contact-list-item.ts +0 -182
  34. package/src/components/contact-list-item/index.ts +0 -2
  35. package/src/components/content/ds-mobile-content.ts +0 -158
  36. package/src/components/content/index.ts +0 -2
  37. package/src/components/ds-mobile-tabs.css +0 -372
  38. package/src/components/ds-mobile-tabs.ts +0 -217
  39. package/src/components/file-attachment/ds-mobile-file-attachment.ts +0 -164
  40. package/src/components/file-attachment/index.ts +0 -2
  41. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.service.ts +0 -98
  42. package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.ts +0 -514
  43. package/src/components/handbook-detail-modal/index.ts +0 -3
  44. package/src/components/handbook-folder/ds-mobile-handbook-folder-mini.ts +0 -130
  45. package/src/components/handbook-folder/ds-mobile-handbook-folder.ts +0 -444
  46. package/src/components/handbook-folder/index.ts +0 -4
  47. package/src/components/header-content/ds-mobile-header-content.ts +0 -211
  48. package/src/components/header-content/index.ts +0 -2
  49. package/src/components/index.ts +0 -45
  50. package/src/components/inline-photo/ds-mobile-inline-photo.ts +0 -269
  51. package/src/components/inline-photo/index.ts +0 -1
  52. package/src/components/interactive-list-item-inquiry/ds-mobile-interactive-list-item-inquiry.css +0 -60
  53. package/src/components/interactive-list-item-inquiry/ds-mobile-interactive-list-item-inquiry.ts +0 -280
  54. package/src/components/interactive-list-item-inquiry/index.ts +0 -2
  55. package/src/components/interactive-list-item-message/ds-mobile-interactive-list-item-message.ts +0 -197
  56. package/src/components/interactive-list-item-message/index.ts +0 -2
  57. package/src/components/interactive-list-item-post/ds-mobile-interactive-list-item-post.css +0 -70
  58. package/src/components/interactive-list-item-post/ds-mobile-interactive-list-item-post.ts +0 -594
  59. package/src/components/interactive-list-item-post/ds-mobile-post-pdf-attachment.ts +0 -124
  60. package/src/components/interactive-list-item-post/index.ts +0 -13
  61. package/src/components/lightbox/ds-mobile-lightbox-footer.ts +0 -331
  62. package/src/components/lightbox/ds-mobile-lightbox-header.ts +0 -173
  63. package/src/components/lightbox/ds-mobile-lightbox-image.ts +0 -464
  64. package/src/components/lightbox/ds-mobile-lightbox-pdf.css +0 -375
  65. package/src/components/lightbox/ds-mobile-lightbox-pdf.ts +0 -374
  66. package/src/components/lightbox/ds-mobile-lightbox.css +0 -587
  67. package/src/components/lightbox/ds-mobile-lightbox.service.ts +0 -293
  68. package/src/components/lightbox/ds-mobile-lightbox.ts +0 -529
  69. package/src/components/lightbox/index.ts +0 -22
  70. package/src/components/list-item/ds-mobile-list-item.ts +0 -499
  71. package/src/components/list-item/index.ts +0 -2
  72. package/src/components/list-item-static/ds-mobile-list-item-static.ts +0 -133
  73. package/src/components/list-item-static/index.ts +0 -2
  74. package/src/components/logo/ds-logo.ts +0 -85
  75. package/src/components/logo/index.ts +0 -2
  76. package/src/components/modal/ds-mobile-modal.css +0 -163
  77. package/src/components/modal/ds-mobile-modal.service.ts +0 -329
  78. package/src/components/modal/index.ts +0 -8
  79. package/src/components/page-details/ds-mobile-page-details.css +0 -285
  80. package/src/components/page-details/ds-mobile-page-details.ts +0 -128
  81. package/src/components/page-details/index.ts +0 -2
  82. package/src/components/page-main/ds-mobile-page-main.css +0 -346
  83. package/src/components/page-main/ds-mobile-page-main.ts +0 -331
  84. package/src/components/page-main/index.ts +0 -2
  85. package/src/components/post-card/ds-mobile-post-card.ts +0 -685
  86. package/src/components/post-card/ds-mobile-post-pdf-attachment.ts +0 -124
  87. package/src/components/post-card/index.ts +0 -11
  88. package/src/components/post-composer/ds-mobile-post-composer.ts +0 -140
  89. package/src/components/post-composer/index.ts +0 -2
  90. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.service.ts +0 -104
  91. package/src/components/post-detail-modal/ds-mobile-post-detail-modal.ts +0 -1273
  92. package/src/components/post-detail-modal/index.ts +0 -9
  93. package/src/components/shared/directives/index.ts +0 -2
  94. package/src/components/shared/directives/long-press.directive.ts +0 -208
  95. package/src/components/shared/index.ts +0 -3
  96. package/src/components/shared/mobile-common.css +0 -94
  97. package/src/components/shared/mobile-page-base.css +0 -315
  98. package/src/components/shared/mobile-page-base.ts +0 -70
  99. package/src/components/swiper/ds-mobile-swiper.ts +0 -123
  100. package/src/components/swiper/index.ts +0 -2
  101. package/src/components/tab-bar/ds-mobile-tab-bar.ts +0 -132
  102. package/src/components/tab-bar/index.ts +0 -2
  103. package/src/components/tabs/ds-mobile-tabs.css +0 -405
  104. package/src/components/tabs/ds-mobile-tabs.ts +0 -204
  105. package/src/components/tabs/index.ts +0 -2
  106. package/src/pages/community.page.ts +0 -768
  107. package/src/pages/handbook.page.ts +0 -298
  108. package/src/pages/home.page.ts +0 -192
  109. package/src/pages/index.ts +0 -9
  110. package/src/pages/inquiries.example.ts +0 -212
  111. package/src/pages/inquiry-detail.example.css +0 -434
  112. package/src/pages/inquiry-detail.example.ts +0 -416
  113. package/src/pages/mobile-tabs-example.component.ts +0 -146
  114. package/src/pages/post-create.page.ts +0 -311
  115. package/src/pages/post-detail.page.ts +0 -295
  116. package/src/pages/whitelabel-demo.page.ts +0 -548
  117. package/src/public-api.ts +0 -5
  118. package/src/services/user.service.ts +0 -35
  119. package/src/services/whitelabel.service.ts +0 -171
  120. package/src/styles/ionic.css +0 -673
  121. package/tsconfig.lib.json +0 -17
  122. package/tsconfig.lib.prod.json +0 -9
  123. package/tsconfig.spec.json +0 -13
@@ -1,271 +0,0 @@
1
- import { Component, ElementRef, signal, ViewChild, AfterViewInit, input, output, effect, Injector, computed, inject } from '@angular/core';
2
- import { Router } from '@angular/router';
3
- import { CommonModule } from '@angular/common';
4
- import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
5
- import {
6
- IonTabs,
7
- IonTabBar,
8
- IonTabButton,
9
- IonLabel,
10
- IonHeader,
11
- IonToolbar,
12
- IonContent,
13
- IonRefresher,
14
- IonRefresherContent,
15
- Platform
16
- } from '@ionic/angular/standalone';
17
- import { Haptics, ImpactStyle } from '@capacitor/haptics';
18
- import { DsIconComponent } from '@propbinder/design-system';
19
- import { DsAvatarComponent } from '@propbinder/design-system';
20
-
21
- export interface TabConfig {
22
- id: string;
23
- label: string;
24
- route: string;
25
- icon: string;
26
- iconActive: string;
27
- }
28
-
29
- export type HeaderVariant = 'home' | 'simple' | 'back' | 'none';
30
-
31
- /**
32
- * DsMobileAppLayoutComponent
33
- *
34
- * A complete mobile application shell component based on actual mobile page patterns.
35
- * Provides tab navigation, flexible headers, and mobile-optimized layout.
36
- *
37
- * Features:
38
- * - Tab bar navigation with active state
39
- * - Multiple header variants (home with logomark, simple title, back button)
40
- * - Pull-to-refresh support
41
- * - Purple brand background with content wrapper
42
- * - iOS safe area support
43
- *
44
- * @example
45
- * ```html
46
- * <ds-mobile-app-layout
47
- * [showTabBar]="true"
48
- * [tabs]="tabsConfig"
49
- * [headerVariant]="'home'"
50
- * [avatarInitials]="'JD'"
51
- * />
52
- * ```
53
- */
54
- @Component({
55
- selector: 'ds-mobile-app-layout',
56
- standalone: true,
57
- imports: [
58
- CommonModule,
59
- IonTabs,
60
- IonTabBar,
61
- IonTabButton,
62
- IonLabel,
63
- IonHeader,
64
- IonToolbar,
65
- IonContent,
66
- IonRefresher,
67
- IonRefresherContent,
68
- DsIconComponent,
69
- DsAvatarComponent
70
- ],
71
- styleUrls: ['./ds-mobile-app-layout.css'],
72
- template: `
73
- @if (showTabBar()) {
74
- <!-- Layout with tab bar - NO ion-content, pages provide their own -->
75
- <ion-tabs>
76
- <!-- Tab Bar - dynamic slot based on viewport -->
77
- <ion-tab-bar [attr.slot]="isDesktop() ? 'top' : 'bottom'">
78
- @for (tab of tabs(); track tab.id) {
79
- <ion-tab-button
80
- [tab]="tab.id"
81
- (click)="navigateToTab(tab.route)">
82
- <ds-icon
83
- [name]="isTabActive(tab.id) ? tab.iconActive : tab.icon"
84
- size="24px"
85
- />
86
- <ion-label>{{ tab.label }}</ion-label>
87
- </ion-tab-button>
88
- }
89
- </ion-tab-bar>
90
- </ion-tabs>
91
- } @else {
92
- <!-- Standalone layout (no tabs) -->
93
- @if (headerVariant() !== 'none') {
94
- <ion-header>
95
- <ion-toolbar>
96
- @switch (headerVariant()) {
97
- @case ('home') {
98
- <div class="header-home">
99
- <svg class="logomark" width="36" height="32" viewBox="0 0 36 32" fill="none" xmlns="http://www.w3.org/2000/svg">
100
- <path d="M33.9862 5.51709H23.1724V8.82743H26.0413C26.2841 8.82743 26.4827 9.02606 26.4827 9.26881V12.7998C26.4827 13.0426 26.2841 13.2412 26.0413 13.2412H23.1724V14.3447H26.0413C26.2841 14.3447 26.4827 14.5433 26.4827 14.7861V18.3171C26.4827 18.5598 26.2841 18.7585 26.0413 18.7585H23.1724V19.8619H26.0413C26.2841 19.8619 26.4827 20.0605 26.4827 20.3033V23.8343C26.4827 24.0771 26.2841 24.2757 26.0413 24.2757H23.1724V26.2619C23.1724 26.7496 23.0267 27.2043 22.7773 27.5861H27.5862L32 31.9999V27.5861H33.9862C34.7167 27.5861 35.3103 26.9924 35.3103 26.2619V6.84123C35.3103 6.11075 34.7167 5.51709 33.9862 5.51709ZM32 23.8343C32 24.0771 31.8013 24.2757 31.5586 24.2757H28.0276C27.7848 24.2757 27.5862 24.0771 27.5862 23.8343V20.3033C27.5862 20.0605 27.7848 19.8619 28.0276 19.8619H31.5586C31.8013 19.8619 32 20.0605 32 20.3033V23.8343ZM32 18.3171C32 18.5598 31.8013 18.7585 31.5586 18.7585H28.0276C27.7848 18.7585 27.5862 18.5598 27.5862 18.3171V14.7861C27.5862 14.5433 27.7848 14.3447 28.0276 14.3447H31.5586C31.8013 14.3447 32 14.5433 32 14.7861V18.3171ZM32 12.7998C32 13.0426 31.8013 13.2412 31.5586 13.2412H28.0276C27.7848 13.2412 27.5862 13.0426 27.5862 12.7998V9.26881C27.5862 9.02606 27.7848 8.82743 28.0276 8.82743H31.5586C31.8013 8.82743 32 9.02606 32 9.26881V12.7998Z" fill="white"/>
101
- <path d="M20.7448 0H1.32414C0.593655 0 0 0.593655 0 1.32414V26.2621C0 26.9926 0.593655 27.5862 1.32414 27.5862H3.31034V32L7.72414 27.5862H20.7448C21.4753 27.5862 22.069 26.9926 22.069 26.2621V1.32414C22.069 0.593655 21.4753 0 20.7448 0ZM7.72414 23.8345C7.72414 24.0772 7.52552 24.2759 7.28276 24.2759H3.75172C3.50897 24.2759 3.31034 24.0772 3.31034 23.8345V20.3034C3.31034 20.0607 3.50897 19.8621 3.75172 19.8621H7.28276C7.52552 19.8621 7.72414 20.0607 7.72414 20.3034V23.8345ZM7.72414 18.3172C7.72414 18.56 7.52552 18.7586 7.28276 18.7586H3.75172C3.50897 18.7586 3.31034 18.56 3.31034 18.3172V14.7862C3.31034 14.5434 3.50897 14.3448 3.75172 14.3448H7.28276C7.52552 14.3448 7.72414 14.5434 7.72414 14.7862V18.3172ZM7.72414 12.8C7.72414 13.0428 7.52552 13.2414 7.28276 13.2414H3.75172C3.50897 13.2414 3.31034 13.0428 3.31034 12.8V9.26897C3.31034 9.02621 3.50897 8.82759 3.75172 8.82759H7.28276C7.52552 8.82759 7.72414 9.02621 7.72414 9.26897V12.8ZM7.72414 7.28276C7.72414 7.52552 7.52552 7.72414 7.28276 7.72414H3.75172C3.50897 7.72414 3.31034 7.52552 3.31034 7.28276V3.75172C3.31034 3.50897 3.50897 3.31034 3.75172 3.31034H7.28276C7.52552 3.31034 7.72414 3.50897 7.72414 3.75172V7.28276ZM13.2414 23.8345C13.2414 24.0772 13.0428 24.2759 12.8 24.2759H9.26897C9.02621 24.2759 8.82759 24.0772 8.82759 23.8345V20.3034C8.82759 20.0607 9.02621 19.8621 9.26897 19.8621H12.8C13.0428 19.8621 13.2414 20.0607 13.2414 20.3034V23.8345ZM13.2414 18.3172C13.2414 18.56 13.0428 18.7586 12.8 18.7586H9.26897C9.02621 18.7586 8.82759 18.56 8.82759 18.3172V14.7862C8.82759 14.5434 9.02621 14.3448 9.26897 14.3448H12.8C13.0428 14.3448 13.2414 14.5434 13.2414 14.7862V18.3172ZM13.2414 12.8C13.2414 13.0428 13.0428 13.2414 12.8 13.2414H9.26897C9.02621 13.2414 8.82759 13.0428 8.82759 12.8V9.26897C8.82759 9.02621 9.02621 8.82759 9.26897 8.82759H12.8C13.0428 8.82759 13.2414 9.02621 13.2414 9.26897V12.8ZM13.2414 6.84138V7.28276C13.2414 7.52552 13.0428 7.72414 12.8 7.72414H9.26897C9.02621 7.72414 8.82759 7.52552 8.82759 7.28276V3.75172C8.82759 3.50897 9.02621 3.31034 9.26897 3.31034H12.8C13.0428 3.31034 13.2414 3.50897 13.2414 3.75172V6.84138ZM18.7586 23.8345C18.7586 24.0772 18.56 24.2759 18.3172 24.2759H14.7862C14.5434 24.2759 14.3448 24.0772 14.3448 23.8345V20.3034C14.3448 20.0607 14.5434 19.8621 14.7862 19.8621H18.3172C18.56 19.8621 18.7586 20.0607 18.7586 20.3034V23.8345ZM18.7586 18.3172C18.7586 18.56 18.56 18.7586 18.3172 18.7586H14.7862C14.5434 18.7586 14.3448 18.56 14.3448 18.3172V14.7862C14.3448 14.5434 14.5434 14.3448 14.7862 14.3448H18.3172C18.56 14.3448 18.7586 14.5434 18.7586 14.7862V18.3172ZM18.7586 12.8C18.7586 13.0428 18.56 13.2414 18.3172 13.2414H14.7862C14.5434 13.2414 14.3448 13.0428 14.3448 12.8V9.26897C14.3448 9.02621 14.5434 8.82759 14.7862 8.82759H18.3172C18.56 8.82759 18.7586 9.02621 18.7586 9.26897V12.8ZM18.7586 5.51724V7.28276C18.7586 7.52552 18.56 7.72414 18.3172 7.72414H14.7862C14.5434 7.72414 14.3448 7.52552 14.3448 7.28276V3.75172C14.3448 3.50897 14.5434 3.31034 14.7862 3.31034H18.3172C18.56 3.31034 18.7586 3.50897 18.7586 3.75172V5.51724Z" fill="white"/>
102
- </svg>
103
- <div class="header-home__actions">
104
- <ds-avatar
105
- [size]="'md'"
106
- [type]="avatarType()"
107
- [initials]="avatarInitials()"
108
- [src]="avatarSrc()"
109
- [iconName]="avatarIconName()"
110
- (click)="handleAvatarClick()"
111
- />
112
- </div>
113
- </div>
114
- }
115
- @case ('back') {
116
- <div class="header-back">
117
- @if (showBackButton()) {
118
- <button class="back-button" (click)="handleBackClick()">
119
- <ds-icon name="remixArrowLeftSLine" size="24px" />
120
- </button>
121
- }
122
- <h1 class="header-title">{{ pageTitle() }}</h1>
123
- </div>
124
- }
125
- @case ('simple') {
126
- <div class="header-simple">
127
- <h1 class="header-title">{{ pageTitle() }}</h1>
128
- </div>
129
- }
130
- }
131
- </ion-toolbar>
132
- </ion-header>
133
- }
134
-
135
- <ion-content [scrollEvents]="true" (ionScroll)="handleScroll($event)">
136
- @if (showPullToRefresh() && isNativePlatform()) {
137
- <ion-refresher
138
- slot="fixed"
139
- (ionRefresh)="handleRefresh($event)"
140
- [pullFactor]="0.4"
141
- [pullMin]="80"
142
- [pullMax]="240"
143
- closeDuration="600ms">
144
- <ion-refresher-content
145
- pullingIcon="chevron-down-circle-outline"
146
- refreshingSpinner="lines">
147
- </ion-refresher-content>
148
- </ion-refresher>
149
- }
150
-
151
- <ng-content></ng-content>
152
- </ion-content>
153
- }
154
- `
155
- })
156
- export class DsMobileAppLayoutComponent implements AfterViewInit {
157
- @ViewChild(IonContent, { static: false }) ionContent!: IonContent;
158
-
159
- // Platform detection
160
- private platform = inject(Platform);
161
-
162
- // Computed property to check if running on native platform
163
- isNativePlatform = computed(() =>
164
- this.platform.is('ios') ||
165
- this.platform.is('android') ||
166
- this.platform.is('capacitor')
167
- );
168
-
169
- // Inputs
170
- headerVariant = input<HeaderVariant>('simple');
171
- pageTitle = input<string>('');
172
- showBackButton = input<boolean>(false);
173
- showPullToRefresh = input<boolean>(false);
174
- showTabBar = input<boolean>(false);
175
- tabs = input<TabConfig[]>([]);
176
-
177
- // Avatar inputs (for home header)
178
- avatarType = input<'initials' | 'photo' | 'icon'>('initials');
179
- avatarInitials = input<string>('U');
180
- avatarSrc = input<string>('');
181
- avatarIconName = input<string>('remixUser3Line');
182
-
183
- // Outputs
184
- backClick = output<void>();
185
- refresh = output<any>();
186
- avatarClick = output<void>();
187
- scroll = output<any>();
188
-
189
- // Internal state
190
- private scrollY = signal(0);
191
- activeTab = signal<string>('home');
192
- isDesktop = signal<boolean>(false);
193
-
194
- constructor(
195
- private router: Router,
196
- private elementRef: ElementRef,
197
- private breakpointObserver: BreakpointObserver
198
- ) {
199
- // Track active tab based on current route
200
- this.router.events.subscribe(() => {
201
- const url = this.router.url;
202
- const tab = this.tabs().find(t => url.includes(t.route));
203
- if (tab) {
204
- this.activeTab.set(tab.id);
205
- }
206
- });
207
-
208
- // Track breakpoint for tab bar positioning
209
- this.breakpointObserver.observe(['(min-width: 768px)'])
210
- .subscribe(result => {
211
- this.isDesktop.set(result.matches);
212
- });
213
- }
214
-
215
- ngAfterViewInit(): void {
216
- if (this.ionContent) {
217
- this.ionContent.scrollEvents = true;
218
- this.ensureScrollable();
219
- }
220
- }
221
-
222
- handleScroll(event: any): void {
223
- const scrollTop = event.detail.scrollTop || 0;
224
- this.scrollY.set(scrollTop);
225
- this.scroll.emit(event);
226
- }
227
-
228
- async handleRefresh(event: any): Promise<void> {
229
- // Haptic feedback for pull-to-refresh
230
- try {
231
- await Haptics.impact({ style: ImpactStyle.Medium });
232
- } catch {
233
- // Fallback to Web Vibration API if Capacitor Haptics is not available
234
- if ('vibrate' in navigator) {
235
- navigator.vibrate(50);
236
- }
237
- }
238
-
239
- this.refresh.emit(event);
240
- // Note: Parent component should handle event.target.complete()
241
- // If no handler is provided, you can add a default timeout in the parent
242
- }
243
-
244
- handleBackClick(): void {
245
- this.backClick.emit();
246
- }
247
-
248
- handleAvatarClick(): void {
249
- this.avatarClick.emit();
250
- }
251
-
252
- navigateToTab(route: string): void {
253
- this.router.navigateByUrl(route, { replaceUrl: true });
254
- }
255
-
256
- private ensureScrollable(): void {
257
- if (this.ionContent) {
258
- this.ionContent.getScrollElement().then((scrollEl: HTMLElement) => {
259
- if (scrollEl) {
260
- scrollEl.style.overscrollBehaviorY = 'none';
261
- (scrollEl.style as any).webkitOverflowScrolling = 'touch';
262
- }
263
- });
264
- }
265
- }
266
-
267
- isTabActive(tabId: string): boolean {
268
- return this.activeTab() === tabId;
269
- }
270
- }
271
-
@@ -1,2 +0,0 @@
1
- export * from './ds-mobile-app-layout';
2
-
@@ -1,130 +0,0 @@
1
- import { Component, Input, computed, ViewEncapsulation } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { DsAvatarComponent } from '@propbinder/design-system';
4
- import { DsLogoComponent } from '../logo/ds-logo';
5
-
6
- export type AvatarType = 'initials' | 'photo' | 'icon';
7
- export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
8
- export type BadgePosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
9
-
10
- /**
11
- * DsAvatarWithBadgeComponent
12
- *
13
- * Displays an avatar with a logomark badge overlay.
14
- * Useful for showing user avatars with organization branding.
15
- *
16
- * @example
17
- * ```html
18
- * <ds-avatar-with-badge
19
- * [type]="'initials'"
20
- * [initials]="'JD'"
21
- * [size]="'lg'"
22
- * [badgePosition]="'bottom-right'"
23
- * />
24
- * ```
25
- */
26
- @Component({
27
- selector: 'ds-avatar-with-badge',
28
- standalone: true,
29
- imports: [CommonModule, DsAvatarComponent, DsLogoComponent],
30
- encapsulation: ViewEncapsulation.Emulated,
31
- styles: [`
32
- :host {
33
- display: inline-block;
34
- position: relative;
35
- }
36
-
37
- .avatar-badge-container {
38
- position: relative;
39
- display: inline-block;
40
- }
41
-
42
- .avatar-badge {
43
- position: absolute;
44
- background: white;
45
- border-radius: 50%;
46
- display: flex;
47
- align-items: center;
48
- justify-content: center;
49
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
50
- }
51
-
52
- /* Badge positioning */
53
- .avatar-badge--bottom-right {
54
- bottom: 0;
55
- right: 0;
56
- }
57
-
58
- .avatar-badge--bottom-left {
59
- bottom: 0;
60
- left: 0;
61
- }
62
-
63
- .avatar-badge--top-right {
64
- top: 0;
65
- right: 0;
66
- }
67
-
68
- .avatar-badge--top-left {
69
- top: 0;
70
- left: 0;
71
- }
72
-
73
- /* Badge sizes based on avatar size */
74
- .avatar-badge--xs { width: 16px; height: 16px; padding: 3px; }
75
- .avatar-badge--sm { width: 20px; height: 20px; padding: 3px; }
76
- .avatar-badge--md { width: 24px; height: 24px; padding: 4px; }
77
- .avatar-badge--lg { width: 32px; height: 32px; padding: 5px; }
78
- .avatar-badge--xl { width: 40px; height: 40px; padding: 6px; }
79
- `],
80
- template: `
81
- <div class="avatar-badge-container">
82
- <ds-avatar
83
- [type]="type"
84
- [size]="size"
85
- [initials]="initials"
86
- [src]="src"
87
- [iconName]="iconName"
88
- />
89
-
90
- @if (showBadge) {
91
- <div [class]="badgeClasses()">
92
- <ds-logo
93
- variant="mark"
94
- [customHeight]="badgeLogoSize()"
95
- [customWidth]="badgeLogoSize()"
96
- />
97
- </div>
98
- }
99
- </div>
100
- `
101
- })
102
- export class DsAvatarWithBadgeComponent {
103
- // Avatar props
104
- @Input() type: AvatarType = 'initials';
105
- @Input() size: AvatarSize = 'md';
106
- @Input() initials: string = '';
107
- @Input() src: string = '';
108
- @Input() iconName: string = 'remixUser3Fill';
109
-
110
- // Badge props
111
- @Input() showBadge: boolean = true;
112
- @Input() badgePosition: BadgePosition = 'bottom-right';
113
-
114
- badgeClasses = computed(() => {
115
- return `avatar-badge avatar-badge--${this.badgePosition} avatar-badge--${this.size}`;
116
- });
117
-
118
- // Calculate badge logo size based on avatar size
119
- badgeLogoSize = computed(() => {
120
- const sizeMap: Record<AvatarSize, number> = {
121
- xs: 10,
122
- sm: 14,
123
- md: 16,
124
- lg: 22,
125
- xl: 28
126
- };
127
- return sizeMap[this.size];
128
- });
129
- }
130
-
@@ -1,2 +0,0 @@
1
- export * from './ds-avatar-with-badge';
2
-
@@ -1,273 +0,0 @@
1
- import { Component, Input, computed, signal } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { ModalController } from '@ionic/angular/standalone';
4
- import { DsIconComponent } from '@propbinder/design-system';
5
- import { DsMobileActionListItemComponent } from '../action-list-item/ds-mobile-action-list-item';
6
-
7
- export interface ActionResult {
8
- action: string;
9
- }
10
-
11
- export interface ActionItem {
12
- action: string;
13
- title: string;
14
- icon: string;
15
- destructive?: boolean;
16
- }
17
-
18
- export interface ActionGroup {
19
- actions: ActionItem[];
20
- }
21
-
22
- /**
23
- * DsMobileActionsBottomSheetComponent
24
- *
25
- * Generic bottom sheet for displaying action lists.
26
- * Supports custom action groups or preset content actions (posts/comments).
27
- * Action groups are automatically separated by full-width dividers.
28
- *
29
- * @example Custom actions with auto-height (recommended to avoid cropping)
30
- * ```typescript
31
- * const sheet = await this.modalController.create({
32
- * component: DsMobileActionsBottomSheetComponent,
33
- * componentProps: {
34
- * customActionGroups: [
35
- * {
36
- * actions: [
37
- * { action: 'profile', title: 'Min profil', icon: 'remixUser3Line' },
38
- * { action: 'settings', title: 'Indstillinger', icon: 'remixSettings3Line' }
39
- * ]
40
- * },
41
- * {
42
- * actions: [
43
- * { action: 'logout', title: 'Log ud', icon: 'remixLogoutBoxLine', destructive: true }
44
- * ]
45
- * }
46
- * ]
47
- * },
48
- * breakpoints: [0, 1],
49
- * initialBreakpoint: 1,
50
- * handle: true,
51
- * cssClass: 'auto-height'
52
- * });
53
- *
54
- * const result = await sheet.onWillDismiss();
55
- * if (result.data?.action) {
56
- * // Handle the action
57
- * }
58
- * ```
59
- *
60
- * @example Preset content actions
61
- * ```typescript
62
- * const sheet = await this.modalController.create({
63
- * component: DsMobileActionsBottomSheetComponent,
64
- * componentProps: {
65
- * isOwnContent: false
66
- * },
67
- * breakpoints: [0, 1],
68
- * initialBreakpoint: 1,
69
- * handle: true,
70
- * cssClass: 'auto-height'
71
- * });
72
- * ```
73
- */
74
- @Component({
75
- selector: 'ds-mobile-actions-bottom-sheet',
76
- standalone: true,
77
- imports: [CommonModule, DsIconComponent, DsMobileActionListItemComponent],
78
- template: `
79
- <div class="actions-sheet">
80
- <!-- Actions List -->
81
- <div class="actions-list">
82
- @for (group of actionGroups(); track $index; let isLast = $last) {
83
- <!-- Action Group -->
84
- <div class="action-group">
85
- @for (actionItem of group.actions; track actionItem.action; let isLastInGroup = $last) {
86
- <ds-mobile-action-list-item
87
- [title]="actionItem.title"
88
- [showDivider]="!isLastInGroup"
89
- [class.destructive]="actionItem.destructive"
90
- (itemClick)="selectAction(actionItem.action)">
91
- <ds-icon
92
- action-icon
93
- [name]="actionItem.icon"
94
- size="20px"
95
- [class.destructive-icon]="actionItem.destructive" />
96
- </ds-mobile-action-list-item>
97
- }
98
- </div>
99
-
100
- <!-- Full-width divider between groups -->
101
- @if (!isLast) {
102
- <div class="action-group-divider"></div>
103
- }
104
- }
105
- </div>
106
- </div>
107
- `,
108
- styles: [`
109
- :host {
110
- display: block;
111
- }
112
-
113
- .actions-sheet {
114
- display: flex;
115
- flex-direction: column;
116
- background: var(--color-background-neutral-primary, #ffffff);
117
- padding-bottom: env(safe-area-inset-bottom, 0px);
118
- }
119
-
120
- /* Actions List */
121
- .actions-list {
122
- display: flex;
123
- flex-direction: column;
124
- padding-top: 16px; /* Only top padding for the container */
125
- }
126
-
127
- /* Action Group - padding on groups instead of list */
128
- .action-group {
129
- display: flex;
130
- flex-direction: column;
131
- padding: 0 16px; /* Horizontal padding on each group */
132
- }
133
-
134
- /* Override default background color to transparent so hover state is visible */
135
- /* Need ::ng-deep because ds-mobile-list-item uses :host styles internally */
136
- ::ng-deep ds-mobile-action-list-item ds-mobile-list-item {
137
- --color-background-primary: transparent;
138
- --color-background-neutral-primary-hover: var(--color-background-neutral-secondary, #f5f5f5);
139
- }
140
-
141
- /* Ensure the interactive background is visible */
142
- ::ng-deep ds-mobile-action-list-item ds-mobile-list-item .list-item-inner::before {
143
- z-index: 0 !important;
144
- }
145
-
146
- /* Ensure content is above the background */
147
- ::ng-deep ds-mobile-action-list-item ds-mobile-list-item .content-leading,
148
- ::ng-deep ds-mobile-action-list-item ds-mobile-list-item .content-main,
149
- ::ng-deep ds-mobile-action-list-item ds-mobile-list-item .content-trailing {
150
- position: relative;
151
- z-index: 1;
152
- }
153
-
154
- /* Hide divider on last list item using CSS variable */
155
- ds-mobile-action-list-item:last-of-type {
156
- --divider-display: none;
157
- }
158
-
159
- /* Destructive action styling */
160
- ds-mobile-action-list-item.destructive {
161
- --text-color-default-primary: var(--color-error-base, #ef4444);
162
- }
163
-
164
- .destructive-icon {
165
- color: var(--color-error-base, #ef4444);
166
- }
167
-
168
- /* Full-width divider between action groups */
169
- .action-group-divider {
170
- height: 1px;
171
- background: var(--color-border-subtle, #e5e5e5);
172
- margin: 8px 0;
173
- }
174
- `]
175
- })
176
- export class DsMobileActionsBottomSheetComponent {
177
- /**
178
- * Custom action groups to display (overrides isOwnContent)
179
- */
180
- @Input() customActionGroups?: ActionGroup[];
181
-
182
- /**
183
- * Whether this content belongs to the current user (for preset content actions)
184
- */
185
- @Input() isOwnContent: boolean = false;
186
-
187
- /**
188
- * Computed action groups - uses custom groups if provided, otherwise falls back to preset content actions
189
- */
190
- actionGroups = computed<ActionGroup[]>(() => {
191
- // Use custom action groups if provided
192
- if (this.customActionGroups) {
193
- return this.customActionGroups;
194
- }
195
-
196
- // Otherwise fall back to preset content actions
197
- if (this.isOwnContent) {
198
- // Own content: Group 1 (Edit, Delete) + Group 2 (Like, Reply)
199
- return [
200
- {
201
- actions: [
202
- {
203
- action: 'edit',
204
- title: 'Rediger',
205
- icon: 'remixEditLine',
206
- destructive: false
207
- },
208
- {
209
- action: 'delete',
210
- title: 'Slet',
211
- icon: 'remixDeleteBinLine',
212
- destructive: true
213
- }
214
- ]
215
- },
216
- {
217
- actions: [
218
- {
219
- action: 'like',
220
- title: 'Synes om',
221
- icon: 'remixHeart3Line',
222
- destructive: false
223
- },
224
- {
225
- action: 'reply',
226
- title: 'Svar',
227
- icon: 'remixReplyLine',
228
- destructive: false
229
- }
230
- ]
231
- }
232
- ];
233
- } else {
234
- // Other users' content: Group 1 (Like, Reply)
235
- return [
236
- {
237
- actions: [
238
- {
239
- action: 'like',
240
- title: 'Synes om',
241
- icon: 'remixHeart3Line',
242
- destructive: false
243
- },
244
- {
245
- action: 'reply',
246
- title: 'Svar',
247
- icon: 'remixReplyLine',
248
- destructive: false
249
- }
250
- ]
251
- }
252
- ];
253
- }
254
- });
255
-
256
- constructor(private modalController: ModalController) {}
257
-
258
- /**
259
- * Handle action selection and dismiss with result
260
- */
261
- selectAction(action: string): void {
262
- this.modalController.dismiss(
263
- { action } as ActionResult,
264
- 'select'
265
- );
266
- }
267
- }
268
-
269
- // Legacy exports for backwards compatibility
270
- export { DsMobileActionsBottomSheetComponent as DsMobilePostActionsBottomSheetComponent };
271
- export { DsMobileActionsBottomSheetComponent as DsMobileCommentActionsBottomSheetComponent };
272
- export type { ActionResult as PostActionResult };
273
- export type { ActionResult as CommentActionResult };