@propbinder/mobile-design 0.2.50 → 0.2.53
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.
- package/fesm2022/propbinder-mobile-design.mjs +26206 -0
- package/fesm2022/propbinder-mobile-design.mjs.map +1 -0
- package/index.d.ts +8193 -0
- package/package.json +39 -3
- package/ng-package.json +0 -24
- package/src/animations/page-transitions.ts +0 -165
- package/src/components/action-list-item/ds-mobile-action-list-item.ts +0 -102
- package/src/components/action-list-item/index.ts +0 -2
- package/src/components/app-icon/ds-app-icon.ts +0 -133
- package/src/components/app-icon/index.ts +0 -2
- package/src/components/attachment-preview/ds-mobile-attachment-preview.css +0 -139
- package/src/components/attachment-preview/ds-mobile-attachment-preview.ts +0 -164
- package/src/components/attachment-preview/index.ts +0 -1
- package/src/components/avatar-with-badge/ds-avatar-with-badge.ts +0 -142
- package/src/components/avatar-with-badge/index.ts +0 -2
- package/src/components/booking-modal/ds-mobile-booking-confirmation-wrapper.ts +0 -71
- package/src/components/booking-modal/ds-mobile-booking-modal.service.ts +0 -121
- package/src/components/booking-modal/ds-mobile-booking-modal.ts +0 -598
- package/src/components/booking-modal/ds-mobile-booking-summary.ts +0 -161
- package/src/components/booking-modal/index.ts +0 -4
- package/src/components/bottom-sheet/ds-mobile-actions-bottom-sheet.ts +0 -266
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet-header.ts +0 -146
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet-wrapper.ts +0 -156
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet.css +0 -101
- package/src/components/bottom-sheet/ds-mobile-bottom-sheet.service.ts +0 -169
- package/src/components/bottom-sheet/ds-mobile-confirmation-sheet.ts +0 -211
- package/src/components/bottom-sheet/ds-mobile-post-create-bottom-sheet.ts +0 -578
- package/src/components/bottom-sheet/ds-mobile-profile-actions-sheet.ts +0 -614
- package/src/components/bottom-sheet/index.ts +0 -8
- package/src/components/bottom-sheet/modal-shadow-fix.ts +0 -42
- package/src/components/card-inline/ds-mobile-card-inline.ts +0 -301
- package/src/components/card-inline/index.ts +0 -2
- package/src/components/card-inline-banner/ds-mobile-card-inline-banner.ts +0 -118
- package/src/components/card-inline-banner/index.ts +0 -1
- package/src/components/card-inline-contact/ds-mobile-card-inline-contact.ts +0 -120
- package/src/components/card-inline-contact/index.ts +0 -1
- package/src/components/card-inline-file/ds-mobile-card-inline-file.ts +0 -141
- package/src/components/card-inline-file/index.ts +0 -1
- package/src/components/chat-modal/ds-mobile-chat-modal.css +0 -159
- package/src/components/chat-modal/ds-mobile-chat-modal.service.ts +0 -105
- package/src/components/chat-modal/ds-mobile-chat-modal.ts +0 -918
- package/src/components/chat-modal/index.ts +0 -8
- package/src/components/comment/ds-mobile-comment.ts +0 -568
- package/src/components/comment/index.ts +0 -2
- package/src/components/contact-list-item/ds-mobile-contact-list-item.ts +0 -182
- package/src/components/contact-list-item/index.ts +0 -2
- package/src/components/content/ds-mobile-content.ts +0 -139
- package/src/components/content/index.ts +0 -2
- package/src/components/dropdown/ds-mobile-dropdown.css +0 -199
- package/src/components/dropdown/ds-mobile-dropdown.ts +0 -340
- package/src/components/dropdown/index.ts +0 -2
- package/src/components/ds-mobile-tabs.css +0 -407
- package/src/components/ds-mobile-tabs.ts +0 -216
- package/src/components/empty-state/ds-mobile-empty-state.ts +0 -120
- package/src/components/empty-state/index.ts +0 -2
- package/src/components/fab/ds-mobile-fab.ts +0 -315
- package/src/components/fab/index.ts +0 -1
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-confirmation-wrapper.ts +0 -121
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.css +0 -189
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.service.ts +0 -135
- package/src/components/facility-creation-modal/ds-mobile-facility-creation-modal.ts +0 -656
- package/src/components/facility-creation-modal/index.ts +0 -9
- package/src/components/facility-creation-modal/sheets/ds-mobile-access-sheet.ts +0 -105
- package/src/components/facility-creation-modal/sheets/ds-mobile-price-sheet.ts +0 -188
- package/src/components/facility-creation-modal/sheets/ds-mobile-when-can-book-sheet.ts +0 -460
- package/src/components/facility-creation-modal/sheets/ds-mobile-who-can-book-sheet.ts +0 -134
- package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.service.ts +0 -69
- package/src/components/facility-detail-modal/ds-mobile-facility-detail-modal.ts +0 -379
- package/src/components/facility-detail-modal/index.ts +0 -2
- package/src/components/file-attachment/ds-mobile-file-attachment.ts +0 -164
- package/src/components/file-attachment/index.ts +0 -2
- package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.css +0 -214
- package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.service.ts +0 -84
- package/src/components/handbook-detail-modal/ds-mobile-handbook-detail-modal.ts +0 -424
- package/src/components/handbook-detail-modal/index.ts +0 -3
- package/src/components/handbook-folder/ds-mobile-handbook-folder-mini.ts +0 -175
- package/src/components/handbook-folder/ds-mobile-handbook-folder.ts +0 -533
- package/src/components/handbook-folder/index.ts +0 -4
- package/src/components/header-content/ds-mobile-header-content.ts +0 -222
- package/src/components/header-content/index.ts +0 -2
- package/src/components/illustration/ds-mobile-illustration.ts +0 -124
- package/src/components/illustration/index.ts +0 -2
- package/src/components/index.ts +0 -124
- package/src/components/inline-photo/ds-mobile-inline-photo.ts +0 -361
- package/src/components/inline-photo/index.ts +0 -1
- package/src/components/inline-tabs/ds-mobile-inline-tabs.ts +0 -132
- package/src/components/inline-tabs/index.ts +0 -2
- package/src/components/interactive-list-item-booking/ds-mobile-interactive-list-item-booking.ts +0 -350
- package/src/components/interactive-list-item-booking/index.ts +0 -1
- package/src/components/interactive-list-item-inquiry/ds-mobile-interactive-list-item-inquiry.ts +0 -321
- package/src/components/interactive-list-item-inquiry/index.ts +0 -2
- package/src/components/interactive-list-item-message/ds-mobile-interactive-list-item-message.ts +0 -237
- package/src/components/interactive-list-item-message/index.ts +0 -2
- package/src/components/interactive-list-item-post/ds-mobile-interactive-list-item-post.ts +0 -549
- package/src/components/interactive-list-item-post/ds-mobile-post-pdf-attachment.ts +0 -124
- package/src/components/interactive-list-item-post/index.ts +0 -13
- package/src/components/lightbox/ds-mobile-lightbox-footer.ts +0 -315
- package/src/components/lightbox/ds-mobile-lightbox-header.ts +0 -202
- package/src/components/lightbox/ds-mobile-lightbox-image.ts +0 -484
- package/src/components/lightbox/ds-mobile-lightbox-pdf.css +0 -377
- package/src/components/lightbox/ds-mobile-lightbox-pdf.ts +0 -374
- package/src/components/lightbox/ds-mobile-lightbox.css +0 -587
- package/src/components/lightbox/ds-mobile-lightbox.service.ts +0 -296
- package/src/components/lightbox/ds-mobile-lightbox.ts +0 -529
- package/src/components/lightbox/index.ts +0 -22
- package/src/components/list-item/ds-mobile-list-item.ts +0 -603
- package/src/components/list-item/index.ts +0 -2
- package/src/components/list-item-static/ds-mobile-list-item-static.ts +0 -133
- package/src/components/list-item-static/index.ts +0 -2
- package/src/components/loader-overlay/ds-mobile-loader-overlay.css +0 -49
- package/src/components/loader-overlay/ds-mobile-loader-overlay.ts +0 -77
- package/src/components/loader-overlay/index.ts +0 -1
- package/src/components/logo/ds-logo.ts +0 -95
- package/src/components/logo/index.ts +0 -2
- package/src/components/message-bubble/ds-mobile-message-bubble.ts +0 -633
- package/src/components/message-bubble/index.ts +0 -7
- package/src/components/message-composer/ds-mobile-message-composer.ts +0 -1146
- package/src/components/message-composer/index.ts +0 -7
- package/src/components/modal/ds-mobile-modal.css +0 -163
- package/src/components/modal/ds-mobile-modal.service.ts +0 -329
- package/src/components/modal/index.ts +0 -8
- package/src/components/modal-base/ds-mobile-modal-base.css +0 -378
- package/src/components/modal-base/ds-mobile-modal-base.ts +0 -261
- package/src/components/modal-base/index.ts +0 -2
- package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.css +0 -112
- package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.service.ts +0 -93
- package/src/components/new-inquiry-modal/ds-mobile-new-inquiry-modal.ts +0 -442
- package/src/components/new-inquiry-modal/index.ts +0 -4
- package/src/components/offline-banner/ds-mobile-offline-banner.ts +0 -135
- package/src/components/offline-banner/index.ts +0 -1
- package/src/components/page-details/ds-mobile-page-details.css +0 -83
- package/src/components/page-details/ds-mobile-page-details.ts +0 -282
- package/src/components/page-details/index.ts +0 -2
- package/src/components/page-main/ds-mobile-page-main.css +0 -68
- package/src/components/page-main/ds-mobile-page-main.ts +0 -421
- package/src/components/page-main/index.ts +0 -2
- package/src/components/post-composer/ds-mobile-post-composer.ts +0 -140
- package/src/components/post-composer/index.ts +0 -2
- package/src/components/post-detail-modal/ds-mobile-post-detail-modal.css +0 -390
- package/src/components/post-detail-modal/ds-mobile-post-detail-modal.service.ts +0 -108
- package/src/components/post-detail-modal/ds-mobile-post-detail-modal.ts +0 -722
- package/src/components/post-detail-modal/index.ts +0 -9
- package/src/components/property-banner/ds-mobile-property-banner.ts +0 -95
- package/src/components/property-banner/index.ts +0 -2
- package/src/components/section/ds-mobile-section.ts +0 -263
- package/src/components/section/index.ts +0 -2
- package/src/components/shared/directives/index.ts +0 -2
- package/src/components/shared/directives/long-press.directive.ts +0 -212
- package/src/components/shared/index.ts +0 -3
- package/src/components/shared/mobile-modal-base.ts +0 -457
- package/src/components/shared/mobile-page-base.ts +0 -204
- package/src/components/swiper/ds-mobile-swiper-with-nav.ts +0 -160
- package/src/components/swiper/ds-mobile-swiper.ts +0 -327
- package/src/components/swiper/index.ts +0 -3
- package/src/components/system-message-banner/ds-mobile-system-message-banner.ts +0 -129
- package/src/components/system-message-banner/index.ts +0 -2
- package/src/components/tab-bar/ds-mobile-tab-bar.css +0 -533
- package/src/components/tab-bar/ds-mobile-tab-bar.ts +0 -735
- package/src/components/tab-bar/index.ts +0 -2
- package/src/components/tabs/ds-mobile-tabs.css +0 -25
- package/src/components/tabs/ds-mobile-tabs.ts +0 -89
- package/src/components/tabs/index.ts +0 -2
- package/src/components/text-input/ds-text-input.ts +0 -287
- package/src/components/text-input/index.ts +0 -2
- package/src/examples/booking.page.ts +0 -434
- package/src/examples/community.page.ts +0 -776
- package/src/examples/handbook.page.ts +0 -324
- package/src/examples/home.page.ts +0 -347
- package/src/examples/index.ts +0 -12
- package/src/examples/inquiries.example.ts +0 -273
- package/src/examples/inquiry-detail.example.css +0 -189
- package/src/examples/inquiry-detail.example.ts +0 -415
- package/src/examples/mobile-tabs-example.component.ts +0 -208
- package/src/examples/post-create.page.ts +0 -311
- package/src/examples/post-detail.page.ts +0 -296
- package/src/examples/sign-in.page.ts +0 -291
- package/src/examples/whitelabel-demo-modal.component.ts +0 -1094
- package/src/examples/whitelabel-demo-modal.service.ts +0 -77
- package/src/models/index.ts +0 -7
- package/src/models/post.model.ts +0 -41
- package/src/pages/community.page.ts +0 -769
- package/src/pages/handbook.page.ts +0 -388
- package/src/pages/home.page.ts +0 -303
- package/src/pages/index.ts +0 -11
- package/src/pages/inquiries.example.ts +0 -273
- package/src/pages/inquiry-detail.example.css +0 -189
- package/src/pages/inquiry-detail.example.ts +0 -415
- package/src/pages/mobile-tabs-example.component.ts +0 -179
- package/src/pages/post-create.page.ts +0 -311
- package/src/pages/post-detail.page.ts +0 -296
- package/src/pages/sign-in.page.ts +0 -291
- package/src/pages/whitelabel-demo-modal.component.ts +0 -1094
- package/src/pages/whitelabel-demo-modal.service.ts +0 -77
- package/src/public-api.ts +0 -6
- package/src/services/base-modal.service.ts +0 -101
- package/src/services/index.ts +0 -11
- package/src/services/posts.service.ts +0 -542
- package/src/services/tracking-permission.service.ts +0 -88
- package/src/services/user.service.ts +0 -60
- package/src/services/whitelabel.service.ts +0 -675
- package/tsconfig.lib.json +0 -17
- package/tsconfig.lib.prod.json +0 -9
- package/tsconfig.spec.json +0 -13
- /package/{src/assets → assets}/fonts/Brockmann-Bold.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann-BoldItalic.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann-Medium.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann-MediumItalic.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann-Regular.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann-RegularItalic.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann-SemiBold.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann-SemiBoldItalic.otf +0 -0
- /package/{src/assets → assets}/fonts/Brockmann_desktop_license.pdf +0 -0
- /package/{src/assets → assets}/fonts/brockmann-medium-webfont.woff2 +0 -0
- /package/{src/assets → assets}/fonts/brockmann-mediumitalic-webfont.woff2 +0 -0
- /package/{src/assets → assets}/fonts/brockmann-regular-webfont.woff2 +0 -0
- /package/{src/assets → assets}/fonts/brockmann-regularitalic-webfont.woff2 +0 -0
- /package/{src/assets → assets}/fonts/brockmann-semibold-webfont.woff2 +0 -0
- /package/{src/assets → assets}/fonts/brockmann-semibolditalic-webfont.woff2 +0 -0
- /package/{src/styles → styles}/ionic.css +0 -0
- /package/{src/components/shared → styles}/mobile-common.css +0 -0
- /package/{src/components/shared → styles}/mobile-page-base.css +0 -0
|
@@ -1,603 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Component,
|
|
3
|
-
input,
|
|
4
|
-
output,
|
|
5
|
-
computed,
|
|
6
|
-
signal,
|
|
7
|
-
PLATFORM_ID,
|
|
8
|
-
inject,
|
|
9
|
-
ElementRef,
|
|
10
|
-
AfterViewInit,
|
|
11
|
-
} from '@angular/core';
|
|
12
|
-
import { CommonModule, isPlatformBrowser } from '@angular/common';
|
|
13
|
-
import { DsMobileLongPressDirective } from '../shared/directives/long-press.directive';
|
|
14
|
-
import { DsIconButtonComponent } from '@propbinder/design-system';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* DsMobileListItemComponent
|
|
18
|
-
*
|
|
19
|
-
* A versatile, reusable list item component for mobile applications.
|
|
20
|
-
* Supports both interactive and non-interactive modes with flexible content projection.
|
|
21
|
-
*
|
|
22
|
-
* Features:
|
|
23
|
-
* - Interactive mode with click and long-press support
|
|
24
|
-
* - Pseudo-element background extends 8px beyond bounds (no negative margins needed)
|
|
25
|
-
* - Flexible content slots (leading, main, trailing)
|
|
26
|
-
* - Optional structured inputs for common use cases (title, subtitle)
|
|
27
|
-
* - Accessibility features (focus states, ARIA attributes)
|
|
28
|
-
* - Disabled and loading states
|
|
29
|
-
*
|
|
30
|
-
* This component serves as the foundation for specialized list item types like posts,
|
|
31
|
-
* notifications, messages, contacts, and other list content.
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```html
|
|
35
|
-
* <!-- Simple structured usage -->
|
|
36
|
-
* <ds-mobile-list-item
|
|
37
|
-
* title="Document Title"
|
|
38
|
-
* subtitle="Supporting text"
|
|
39
|
-
* [interactive]="true"
|
|
40
|
-
* (itemClick)="handleClick()">
|
|
41
|
-
*
|
|
42
|
-
* <ds-icon content-leading name="document" />
|
|
43
|
-
* </ds-mobile-list-item>
|
|
44
|
-
*
|
|
45
|
-
* <!-- Flexible custom usage -->
|
|
46
|
-
* <ds-mobile-list-item
|
|
47
|
-
* [interactive]="true"
|
|
48
|
-
* (itemClick)="handleClick()"
|
|
49
|
-
* (longPress)="showContextMenu()">
|
|
50
|
-
*
|
|
51
|
-
* <div content-leading>
|
|
52
|
-
* <ds-avatar initials="JD" />
|
|
53
|
-
* </div>
|
|
54
|
-
*
|
|
55
|
-
* <div content-main>
|
|
56
|
-
* <h3>Custom Content</h3>
|
|
57
|
-
* <p>Full control over layout and styling</p>
|
|
58
|
-
* </div>
|
|
59
|
-
*
|
|
60
|
-
* <button content-trailing (click)="handleAction($event)">
|
|
61
|
-
* Action
|
|
62
|
-
* </button>
|
|
63
|
-
* </ds-mobile-list-item>
|
|
64
|
-
*
|
|
65
|
-
* <!-- Non-interactive read-only -->
|
|
66
|
-
* <ds-mobile-list-item
|
|
67
|
-
* title="Read-only Item"
|
|
68
|
-
* subtitle="No interaction">
|
|
69
|
-
* <ds-icon content-leading name="info" />
|
|
70
|
-
* </ds-mobile-list-item>
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
@Component({
|
|
74
|
-
selector: 'ds-mobile-list-item',
|
|
75
|
-
standalone: true,
|
|
76
|
-
imports: [CommonModule, DsIconButtonComponent],
|
|
77
|
-
hostDirectives: [
|
|
78
|
-
{
|
|
79
|
-
directive: DsMobileLongPressDirective,
|
|
80
|
-
outputs: ['longPress'],
|
|
81
|
-
},
|
|
82
|
-
],
|
|
83
|
-
host: {
|
|
84
|
-
'[class.interactive]': 'interactive() && !disabled()',
|
|
85
|
-
'[class.disabled]': 'disabled()',
|
|
86
|
-
'[class.loading]': 'loading()',
|
|
87
|
-
'[class.no-divider]': '!showDivider()',
|
|
88
|
-
'[class.no-leading-content]': '!hasLeadingContent()',
|
|
89
|
-
'[class.variant-compact]': 'variant() === "compact"',
|
|
90
|
-
'[class.align-top]': 'align() === "top"',
|
|
91
|
-
'[class.align-center]': 'align() === "center"',
|
|
92
|
-
'[class.align-bottom]': 'align() === "bottom"',
|
|
93
|
-
'[attr.role]': 'interactive() ? "button" : null',
|
|
94
|
-
'[attr.tabindex]': 'interactive() && !disabled() ? "0" : null',
|
|
95
|
-
'[attr.aria-disabled]': 'disabled() ? "true" : null',
|
|
96
|
-
'[style.--leading-size]': 'leadingSize()',
|
|
97
|
-
'[style.--interactive-offset]': 'interactiveOffset()',
|
|
98
|
-
'[style.--divider-spacing]': 'dividerSpacing()',
|
|
99
|
-
'(click)': 'handleClick($event)',
|
|
100
|
-
'(keydown.enter)': 'handleKeyDown($event)',
|
|
101
|
-
'(keydown.space)': 'handleKeyDown($event)',
|
|
102
|
-
'(longPress)': 'handleLongPress()',
|
|
103
|
-
},
|
|
104
|
-
styles: [
|
|
105
|
-
`
|
|
106
|
-
:host {
|
|
107
|
-
display: block;
|
|
108
|
-
position: relative;
|
|
109
|
-
padding: var(--item-padding-top, 12px) 0
|
|
110
|
-
var(--item-padding-bottom, 12px) 0;
|
|
111
|
-
box-sizing: border-box;
|
|
112
|
-
/* CSS variables defined at host level for use by children and pseudo-elements */
|
|
113
|
-
--leading-size: 32px;
|
|
114
|
-
--content-gap: 12px;
|
|
115
|
-
--interactive-offset: 8px;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/* Divider line on host */
|
|
119
|
-
:host::after {
|
|
120
|
-
content: '';
|
|
121
|
-
position: absolute;
|
|
122
|
-
bottom: 0;
|
|
123
|
-
left: calc(var(--leading-size) + var(--content-gap));
|
|
124
|
-
right: 0;
|
|
125
|
-
height: 1px;
|
|
126
|
-
background: var(--border-color-default, #e5e5e5);
|
|
127
|
-
z-index: 1;
|
|
128
|
-
display: var(--divider-display, block);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/* Hide divider when no-divider class is present */
|
|
132
|
-
:host(.no-divider)::after {
|
|
133
|
-
display: none;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/* Adjust divider when no leading content */
|
|
137
|
-
:host(.no-leading-content)::after {
|
|
138
|
-
left: 0;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.list-item-inner {
|
|
142
|
-
display: flex;
|
|
143
|
-
flex-direction: row;
|
|
144
|
-
align-items: flex-start;
|
|
145
|
-
gap: var(--content-gap);
|
|
146
|
-
position: relative;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
:host(.align-center) .list-item-inner {
|
|
150
|
-
align-items: center;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
:host(.align-bottom) .list-item-inner {
|
|
154
|
-
align-items: flex-end;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/* Pseudo-element for interactive background */
|
|
158
|
-
:host(.interactive) .list-item-inner::before {
|
|
159
|
-
content: '';
|
|
160
|
-
position: absolute;
|
|
161
|
-
top: calc(-1 * var(--interactive-offset));
|
|
162
|
-
left: calc(-1 * var(--interactive-offset));
|
|
163
|
-
right: calc(-1 * var(--interactive-offset));
|
|
164
|
-
bottom: calc(-1 * var(--interactive-offset));
|
|
165
|
-
background: var(--color-background-neutral-primary, #ffffff);
|
|
166
|
-
border-radius: 16px;
|
|
167
|
-
z-index: -1;
|
|
168
|
-
pointer-events: none;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/* Interactive states */
|
|
172
|
-
:host(.interactive) {
|
|
173
|
-
cursor: pointer;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/* Hover state (desktop only) */
|
|
177
|
-
@media (hover: hover) and (pointer: fine) {
|
|
178
|
-
:host(.interactive):hover .list-item-inner::before {
|
|
179
|
-
background: var(--color-background-neutral-primary-hover, #f5f5f5);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/* Active state */
|
|
184
|
-
:host(.interactive):active .list-item-inner::before {
|
|
185
|
-
background: var(--color-background-neutral-primary-hover, #f5f5f5);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/* Focus visible for keyboard navigation */
|
|
189
|
-
:host(.interactive):focus-visible {
|
|
190
|
-
outline: none;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
:host(.interactive):focus-visible .list-item-inner::before {
|
|
194
|
-
outline: 2px solid var(--color-brand-primary, #5d5fef);
|
|
195
|
-
outline-offset: 2px;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/* Disabled state */
|
|
199
|
-
:host(.disabled) {
|
|
200
|
-
opacity: 0.5;
|
|
201
|
-
pointer-events: none;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/* Loading state */
|
|
205
|
-
:host(.loading) {
|
|
206
|
-
pointer-events: none;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/* Variants */
|
|
210
|
-
:host(.variant-compact) .list-item-inner {
|
|
211
|
-
gap: 8px;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/* Content slots */
|
|
215
|
-
.content-leading {
|
|
216
|
-
flex-shrink: 0;
|
|
217
|
-
width: var(--leading-size);
|
|
218
|
-
height: var(--leading-size);
|
|
219
|
-
display: flex;
|
|
220
|
-
align-items: flex-start;
|
|
221
|
-
justify-content: center;
|
|
222
|
-
position: relative;
|
|
223
|
-
z-index: 1;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
:host(.align-center) .content-leading {
|
|
227
|
-
align-items: center;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
:host(.align-bottom) .content-leading {
|
|
231
|
-
align-items: flex-end;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
.content-main {
|
|
235
|
-
flex: 1;
|
|
236
|
-
min-width: 0;
|
|
237
|
-
display: flex;
|
|
238
|
-
flex-direction: column;
|
|
239
|
-
gap: 8px;
|
|
240
|
-
position: relative;
|
|
241
|
-
z-index: 1;
|
|
242
|
-
justify-content: flex-start;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
:host(.align-center) .content-main {
|
|
246
|
-
justify-content: center;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
:host(.align-bottom) .content-main {
|
|
250
|
-
justify-content: flex-end;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
.content-trailing {
|
|
254
|
-
flex-shrink: 0;
|
|
255
|
-
display: flex;
|
|
256
|
-
align-items: flex-start;
|
|
257
|
-
position: relative;
|
|
258
|
-
z-index: 1;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/* Structured content styles */
|
|
262
|
-
.structured-title {
|
|
263
|
-
font-family: 'Brockmann', sans-serif;
|
|
264
|
-
font-size: var(--font-size-sm, 14px);
|
|
265
|
-
font-weight: 600;
|
|
266
|
-
line-height: 20px;
|
|
267
|
-
letter-spacing: -0.3px;
|
|
268
|
-
color: var(--text-color-default-primary, #202227);
|
|
269
|
-
margin: 0;
|
|
270
|
-
white-space: nowrap;
|
|
271
|
-
overflow: hidden;
|
|
272
|
-
text-overflow: ellipsis;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
.structured-subtitle {
|
|
276
|
-
font-family: 'Brockmann', sans-serif;
|
|
277
|
-
font-size: var(--font-size-sm, 14px);
|
|
278
|
-
font-weight: 400;
|
|
279
|
-
line-height: 20px;
|
|
280
|
-
letter-spacing: -0.3px;
|
|
281
|
-
color: var(--text-color-default-secondary, #545b66);
|
|
282
|
-
margin: 0;
|
|
283
|
-
white-space: nowrap;
|
|
284
|
-
overflow: hidden;
|
|
285
|
-
text-overflow: ellipsis;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/* Desktop more actions button - using ds-icon-button */
|
|
289
|
-
.desktop-more-button::ng-deep button {
|
|
290
|
-
border-radius: 50% !important;
|
|
291
|
-
}
|
|
292
|
-
`,
|
|
293
|
-
],
|
|
294
|
-
template: `
|
|
295
|
-
<div class="list-item-inner">
|
|
296
|
-
@if (hasLeadingContent()) {
|
|
297
|
-
<div class="content-leading">
|
|
298
|
-
<ng-content select="[content-leading]" />
|
|
299
|
-
</div>
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
<div class="content-main">
|
|
303
|
-
@if (title()) {
|
|
304
|
-
<h3 class="structured-title">{{ title() }}</h3>
|
|
305
|
-
} @if (subtitle()) {
|
|
306
|
-
<p class="structured-subtitle">{{ subtitle() }}</p>
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
<ng-content select="[content-main]" />
|
|
310
|
-
<ng-content />
|
|
311
|
-
</div>
|
|
312
|
-
|
|
313
|
-
<div class="content-trailing">
|
|
314
|
-
@if (interactive() && shouldShowMoreButton()) {
|
|
315
|
-
<ds-icon-button
|
|
316
|
-
class="desktop-more-button"
|
|
317
|
-
icon="remixMoreFill"
|
|
318
|
-
variant="secondary"
|
|
319
|
-
size="sm"
|
|
320
|
-
(clicked)="handleMoreButtonClick($event)"
|
|
321
|
-
aria-label="More options"
|
|
322
|
-
>
|
|
323
|
-
</ds-icon-button>
|
|
324
|
-
}
|
|
325
|
-
<ng-content select="[content-trailing]" />
|
|
326
|
-
</div>
|
|
327
|
-
</div>
|
|
328
|
-
`,
|
|
329
|
-
})
|
|
330
|
-
export class DsMobileListItemComponent implements AfterViewInit {
|
|
331
|
-
private platformId = inject(PLATFORM_ID);
|
|
332
|
-
private elementRef = inject(ElementRef);
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Detect if viewport is desktop size
|
|
336
|
-
* Use viewport width for breakpoint detection (show button on tablet and above)
|
|
337
|
-
*/
|
|
338
|
-
isDesktop = signal<boolean>(false);
|
|
339
|
-
|
|
340
|
-
constructor() {
|
|
341
|
-
if (isPlatformBrowser(this.platformId)) {
|
|
342
|
-
// Show button on tablet breakpoint and above (768px+)
|
|
343
|
-
const isDesktopViewport = window.innerWidth >= 768;
|
|
344
|
-
|
|
345
|
-
// console.log('[ListItem] Desktop detection:', {
|
|
346
|
-
// innerWidth: window.innerWidth,
|
|
347
|
-
// isDesktopViewport
|
|
348
|
-
// });
|
|
349
|
-
|
|
350
|
-
this.isDesktop.set(isDesktopViewport);
|
|
351
|
-
|
|
352
|
-
// Listen for window resize to update detection
|
|
353
|
-
window.addEventListener('resize', () => {
|
|
354
|
-
const newIsDesktop = window.innerWidth >= 768;
|
|
355
|
-
if (newIsDesktop !== this.isDesktop()) {
|
|
356
|
-
// console.log('[ListItem] Viewport changed, updating desktop detection:', newIsDesktop);
|
|
357
|
-
this.isDesktop.set(newIsDesktop);
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
ngAfterViewInit(): void {
|
|
364
|
-
// Check if there's actual content in the leading slot
|
|
365
|
-
this.checkLeadingContent();
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Check if leading content slot has actual content projected
|
|
370
|
-
*/
|
|
371
|
-
private checkLeadingContent(): void {
|
|
372
|
-
if (!isPlatformBrowser(this.platformId)) {
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const hostElement = this.elementRef.nativeElement;
|
|
377
|
-
const leadingElements = hostElement.querySelectorAll('[content-leading]');
|
|
378
|
-
|
|
379
|
-
// Check if any element with content-leading attribute exists and has content
|
|
380
|
-
const hasContent = Array.from(leadingElements).some((el: any) => {
|
|
381
|
-
return el.childNodes.length > 0 || el.textContent?.trim().length > 0;
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
this.hasLeadingContent.set(hasContent);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* CSS size value for the leading content area (e.g., '32px', '40px', '48px')
|
|
389
|
-
* Defaults to '32px' for standard list item avatars/icons
|
|
390
|
-
*/
|
|
391
|
-
leadingSize = input<string>('32px');
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Display variant
|
|
395
|
-
* - undefined (default) - Standard display
|
|
396
|
-
* - 'compact' - Compact display for nested/related items
|
|
397
|
-
*/
|
|
398
|
-
variant = input<'compact' | undefined>(undefined);
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* Vertical alignment of leading and main content slots
|
|
402
|
-
* - 'top' - Align to top (default)
|
|
403
|
-
* - 'center' - Align to center
|
|
404
|
-
* - 'bottom' - Align to bottom
|
|
405
|
-
*/
|
|
406
|
-
align = input<'top' | 'center' | 'bottom'>('top');
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* Whether the list item is interactive (clickable and long-pressable)
|
|
410
|
-
* When true, adds interactive background, cursor pointer, and touch handlers
|
|
411
|
-
*/
|
|
412
|
-
interactive = input<boolean>(false);
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* Whether the list item is disabled
|
|
416
|
-
* Disables all interactions and reduces opacity
|
|
417
|
-
*/
|
|
418
|
-
disabled = input<boolean>(false);
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* Whether the list item is in a loading state
|
|
422
|
-
* Disables interactions but maintains full opacity
|
|
423
|
-
*/
|
|
424
|
-
loading = input<boolean>(false);
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Enable long-press interaction when interactive is true
|
|
428
|
-
* Set to false to disable long-press but keep click
|
|
429
|
-
*/
|
|
430
|
-
enableLongPress = input<boolean>(true);
|
|
431
|
-
|
|
432
|
-
/**
|
|
433
|
-
* Show "more actions" button on desktop for items with long-press enabled
|
|
434
|
-
* @deprecated Use `moreActions` instead. Kept for backwards compatibility.
|
|
435
|
-
* @default true
|
|
436
|
-
*/
|
|
437
|
-
showDesktopMoreButton = input<boolean>(true);
|
|
438
|
-
|
|
439
|
-
/**
|
|
440
|
-
* Unified toggle for contextual actions (more button + long press).
|
|
441
|
-
* - `true` — more button visible on **all** breakpoints, long press enabled
|
|
442
|
-
* - `false` — more button hidden, long press suppressed
|
|
443
|
-
* - `undefined` (default) — falls back to legacy `enableLongPress` + `showDesktopMoreButton` + desktop check
|
|
444
|
-
*/
|
|
445
|
-
moreActions = input<boolean | undefined>(undefined);
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* Resolved visibility of the more-actions button.
|
|
449
|
-
* When `moreActions` is set it takes precedence; otherwise legacy inputs + breakpoint apply.
|
|
450
|
-
*/
|
|
451
|
-
shouldShowMoreButton = computed(() => {
|
|
452
|
-
const ma = this.moreActions();
|
|
453
|
-
if (ma !== undefined) return ma;
|
|
454
|
-
return this.enableLongPress() && this.showDesktopMoreButton() && this.isDesktop();
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* Offset distance for the interactive background pseudo-element
|
|
459
|
-
* Extends the background beyond the content bounds
|
|
460
|
-
* @default '8px'
|
|
461
|
-
*/
|
|
462
|
-
interactiveOffset = input<string>('8px');
|
|
463
|
-
|
|
464
|
-
/**
|
|
465
|
-
* Optional structured title text
|
|
466
|
-
* Provides a simple way to add title without custom markup
|
|
467
|
-
*/
|
|
468
|
-
title = input<string>();
|
|
469
|
-
|
|
470
|
-
/**
|
|
471
|
-
* Optional structured subtitle text
|
|
472
|
-
* Provides a simple way to add subtitle without custom markup
|
|
473
|
-
*/
|
|
474
|
-
subtitle = input<string>();
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* Whether to show the divider line below the list item
|
|
478
|
-
* Automatically hidden on last-child and detail variant
|
|
479
|
-
* @default true
|
|
480
|
-
*/
|
|
481
|
-
showDivider = input<boolean>(true);
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* Spacing around the divider (top and bottom padding)
|
|
485
|
-
* @default '4px'
|
|
486
|
-
*/
|
|
487
|
-
dividerSpacing = input<string>('4px');
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* Emits when the list item is clicked (if interactive and not disabled)
|
|
491
|
-
*/
|
|
492
|
-
itemClick = output<void>();
|
|
493
|
-
|
|
494
|
-
/**
|
|
495
|
-
* Emits when the desktop more actions button is clicked
|
|
496
|
-
* This is separate from longPress to give more control to parent components
|
|
497
|
-
* Typically, you can use (longPress) for both mobile and desktop actions
|
|
498
|
-
*/
|
|
499
|
-
moreButtonClick = output<Event>();
|
|
500
|
-
|
|
501
|
-
/**
|
|
502
|
-
* Track if long press was triggered to prevent click
|
|
503
|
-
*/
|
|
504
|
-
private longPressTriggered = false;
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* Check if leading content slot has content
|
|
508
|
-
* Dynamically updated after view initialization
|
|
509
|
-
*/
|
|
510
|
-
hasLeadingContent = signal<boolean>(true);
|
|
511
|
-
|
|
512
|
-
/**
|
|
513
|
-
* Check if trailing content slot has content
|
|
514
|
-
* Always true to maintain consistent layout
|
|
515
|
-
*/
|
|
516
|
-
hasTrailingContent = computed(() => true);
|
|
517
|
-
|
|
518
|
-
/**
|
|
519
|
-
* Handle click events
|
|
520
|
-
*/
|
|
521
|
-
handleClick(event: Event): void {
|
|
522
|
-
// console.log('[ListItem] Click event fired', {
|
|
523
|
-
// interactive: this.interactive(),
|
|
524
|
-
// disabled: this.disabled(),
|
|
525
|
-
// loading: this.loading(),
|
|
526
|
-
// longPressTriggered: this.longPressTriggered,
|
|
527
|
-
// target: event.target
|
|
528
|
-
// });
|
|
529
|
-
|
|
530
|
-
if (!this.interactive() || this.disabled() || this.loading()) {
|
|
531
|
-
// console.log('[ListItem] Click ignored - not interactive or disabled/loading');
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// Don't emit click if it came from an interactive child element
|
|
536
|
-
// (but not the host element itself)
|
|
537
|
-
const target = event.target as HTMLElement;
|
|
538
|
-
const closestInteractive = target.closest(
|
|
539
|
-
'button, a, input, select, textarea, [role="button"]'
|
|
540
|
-
);
|
|
541
|
-
|
|
542
|
-
// Only block if the interactive element is a REAL child action (like a button in trailing slot),
|
|
543
|
-
// but NOT if it's just the host itself or something inside leading/main that shouldn't block.
|
|
544
|
-
if (closestInteractive && closestInteractive !== event.currentTarget) {
|
|
545
|
-
// If the target is inside content-leading or content-main, we generally want to allow the click
|
|
546
|
-
const isInsidePrimaryContent = !!target.closest('[content-leading], [content-main], .structured-title, .structured-subtitle');
|
|
547
|
-
|
|
548
|
-
if (!isInsidePrimaryContent) {
|
|
549
|
-
// console.log('[ListItem] Click ignored - came from interactive child in trailing/unknown slot:', closestInteractive);
|
|
550
|
-
return;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
if (!this.longPressTriggered) {
|
|
555
|
-
// console.log('[ListItem] Emitting itemClick');
|
|
556
|
-
this.itemClick.emit();
|
|
557
|
-
} else {
|
|
558
|
-
// console.log('[ListItem] Click ignored - long press was triggered');
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
this.longPressTriggered = false;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
/**
|
|
565
|
-
* Handle keyboard events (Enter/Space)
|
|
566
|
-
*/
|
|
567
|
-
handleKeyDown(event: Event): void {
|
|
568
|
-
if (!this.interactive() || this.disabled() || this.loading()) {
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
event.preventDefault();
|
|
573
|
-
this.itemClick.emit();
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
/**
|
|
577
|
-
* Handle long press events from the directive
|
|
578
|
-
* Set the flag to prevent the subsequent click event
|
|
579
|
-
*/
|
|
580
|
-
handleLongPress(): void {
|
|
581
|
-
if (this.moreActions() === false) return;
|
|
582
|
-
this.longPressTriggered = true;
|
|
583
|
-
setTimeout(() => {
|
|
584
|
-
this.longPressTriggered = false;
|
|
585
|
-
}, 100);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/**
|
|
589
|
-
* Handle desktop more button click
|
|
590
|
-
* Stops propagation to prevent triggering itemClick
|
|
591
|
-
* Emits moreButtonClick for parent components to handle
|
|
592
|
-
*/
|
|
593
|
-
handleMoreButtonClick(event: Event): void {
|
|
594
|
-
// console.log('[ListItem] Desktop more button clicked');
|
|
595
|
-
|
|
596
|
-
// Stop propagation to prevent triggering itemClick
|
|
597
|
-
event.stopPropagation();
|
|
598
|
-
event.preventDefault();
|
|
599
|
-
|
|
600
|
-
// Emit the more button click event
|
|
601
|
-
this.moreButtonClick.emit(event);
|
|
602
|
-
}
|
|
603
|
-
}
|