@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,2 @@
1
+ export { DsMobileTabBarComponent, type TabConfig } from './ds-mobile-tab-bar';
2
+
@@ -0,0 +1,25 @@
1
+ /* ============================================
2
+ HOST - ds-mobile-tabs wrapper
3
+ ============================================ */
4
+
5
+ :host {
6
+ display: block;
7
+ height: 100vh;
8
+ height: 100dvh; /* Use dynamic viewport height instead of small viewport height */
9
+ }
10
+
11
+ /* ============================================
12
+ ION-TABS BASE
13
+ ============================================ */
14
+
15
+ ion-tabs {
16
+ height: 100%;
17
+ background: var(--color-header-surface);
18
+ }
19
+
20
+ /* Hide tab bar when detail page is active with slide-out animation (Mobile only) */
21
+ @media (max-width: 767px) {
22
+ ion-tabs:has(ds-mobile-page-details) ds-mobile-tab-bar {
23
+ --tab-bar-transform: translateY(100%);
24
+ }
25
+ }
@@ -0,0 +1,89 @@
1
+ import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import {
4
+ IonTabs,
5
+ IonTab
6
+ } from '@ionic/angular/standalone';
7
+ import { DsMobileTabBarComponent, TabConfig } from '../tab-bar';
8
+
9
+ // Re-export TabConfig for backwards compatibility
10
+ export type { TabConfig } from '../tab-bar';
11
+
12
+ /**
13
+ * DsMobileTabsComponent
14
+ *
15
+ * Responsive tab navigation that adapts from mobile to desktop:
16
+ * - Mobile (< 768px): Bottom tab bar with icons + labels
17
+ * - Desktop (≥ 768px): Top navigation bar with logo, tabs, and avatar
18
+ *
19
+ * Wraps ion-tabs to maintain native routing functionality while
20
+ * providing a responsive navigation experience with branding.
21
+ *
22
+ * NOTE: This component wraps `ion-tabs` internally. If your Angular
23
+ * routing requires `ion-tabs` to be a direct child in your component
24
+ * (e.g., when using `ion-router-outlet` in app.html), use the
25
+ * `DsMobileTabBarComponent` directly inside your own `ion-tabs` instead.
26
+ *
27
+ * @example
28
+ * ```html
29
+ * <ds-mobile-tabs
30
+ * [tabs]="tabsConfig"
31
+ * [avatarInitials]="'JD'"
32
+ * (avatarClick)="handleAvatarClick()"
33
+ * />
34
+ * ```
35
+ */
36
+ @Component({
37
+ selector: 'ds-mobile-tabs',
38
+ standalone: true,
39
+ imports: [
40
+ CommonModule,
41
+ IonTabs,
42
+ IonTab,
43
+ DsMobileTabBarComponent
44
+ ],
45
+ styleUrls: ['./ds-mobile-tabs.css'],
46
+ template: `
47
+ <ion-tabs>
48
+ <ng-container *ngIf="tabs && tabs.length > 0">
49
+ <ion-tab *ngFor="let tab of tabs; trackBy: trackByTabId" [tab]="tab.id"></ion-tab>
50
+ </ng-container>
51
+
52
+ <ds-mobile-tab-bar
53
+ [tabs]="tabs"
54
+ [avatarType]="avatarType"
55
+ [avatarInitials]="avatarInitials"
56
+ [avatarSrc]="avatarSrc"
57
+ [avatarIconName]="avatarIconName"
58
+ (avatarClick)="handleAvatarClick()"
59
+ />
60
+ </ion-tabs>
61
+ `
62
+ })
63
+ export class DsMobileTabsComponent implements OnInit {
64
+ // Inputs
65
+ @Input() tabs: TabConfig[] = [];
66
+
67
+ // Avatar inputs
68
+ @Input() avatarType: 'initials' | 'photo' | 'icon' = 'initials';
69
+ @Input() avatarInitials: string = 'U';
70
+ @Input() avatarSrc: string = '';
71
+ @Input() avatarIconName: string = 'remixUser3Line';
72
+
73
+ // Outputs
74
+ @Output() avatarClick = new EventEmitter<void>();
75
+
76
+ constructor() {}
77
+
78
+ ngOnInit(): void {
79
+ console.log('DsMobileTabsComponent initialized');
80
+ }
81
+
82
+ trackByTabId(index: number, tab: TabConfig): string {
83
+ return tab.id;
84
+ }
85
+
86
+ handleAvatarClick(): void {
87
+ this.avatarClick.emit();
88
+ }
89
+ }
@@ -0,0 +1,2 @@
1
+ export { DsMobileTabsComponent, type TabConfig } from './ds-mobile-tabs';
2
+
@@ -0,0 +1,287 @@
1
+ import { Component, input, output, signal, computed, forwardRef } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
4
+
5
+ /**
6
+ * DsTextInputComponent
7
+ *
8
+ * Mobile-first text input field component following the design system.
9
+ * Supports email, phone, text, and other input types.
10
+ *
11
+ * Features:
12
+ * - All design system states (default, hover, focus, error, disabled)
13
+ * - Validation error state with destructive border color
14
+ * - Automatic error clearing when input becomes valid (configurable)
15
+ * - Built-in validation based on input type or custom validator function
16
+ * - Accessible with proper ARIA attributes
17
+ * - ControlValueAccessor for Angular forms integration
18
+ *
19
+ * @example
20
+ * ```html
21
+ * <!-- Basic usage -->
22
+ * <ds-text-input
23
+ * type="email"
24
+ * placeholder="Enter your email"
25
+ * [(ngModel)]="email">
26
+ * </ds-text-input>
27
+ *
28
+ * <!-- With validation error and auto-clear -->
29
+ * <ds-text-input
30
+ * type="email"
31
+ * placeholder="Enter your email"
32
+ * [hasError]="emailInvalid"
33
+ * errorMessage="Please enter a valid email"
34
+ * [autoClearError]="true"
35
+ * (errorCleared)="emailInvalid = false"
36
+ * [(ngModel)]="email">
37
+ * </ds-text-input>
38
+ *
39
+ * <!-- With custom validator -->
40
+ * <ds-text-input
41
+ * type="text"
42
+ * placeholder="Enter phone number"
43
+ * [validator]="phoneValidator"
44
+ * [hasError]="phoneInvalid"
45
+ * (errorCleared)="phoneInvalid = false"
46
+ * [(ngModel)]="phone">
47
+ * </ds-text-input>
48
+ * ```
49
+ */
50
+ @Component({
51
+ selector: 'ds-text-input',
52
+ standalone: true,
53
+ imports: [CommonModule, FormsModule],
54
+ providers: [
55
+ {
56
+ provide: NG_VALUE_ACCESSOR,
57
+ useExisting: forwardRef(() => DsTextInputComponent),
58
+ multi: true
59
+ }
60
+ ],
61
+ styles: [`
62
+ :host {
63
+ display: block;
64
+ width: 100%;
65
+ }
66
+
67
+ .text-input-wrapper {
68
+ position: relative;
69
+ width: 100%;
70
+ }
71
+
72
+ .text-input {
73
+ width: 100%;
74
+ height: 48px;
75
+ padding: 0 16px;
76
+ box-sizing: border-box;
77
+
78
+ /* Typography */
79
+ font-family: 'Brockmann', system-ui, -apple-system, sans-serif;
80
+ font-size: var(--font-size-base);
81
+ font-weight: 400;
82
+ line-height: 1.4;
83
+ color: var(--text-color-default-primary);
84
+
85
+ /* Background & Border */
86
+ background-color: var(--color-background-neutral-primary);
87
+ border: 1px solid var(--border-color-default);
88
+ border-radius: 8px;
89
+
90
+ /* Transitions */
91
+ transition:
92
+ border-color var(--transition-duration-fast) var(--ease-smooth),
93
+ background-color var(--transition-duration-fast) var(--ease-smooth),
94
+ box-shadow var(--transition-duration-fast) var(--ease-smooth);
95
+
96
+ /* Remove default browser styles */
97
+ outline: none;
98
+ -webkit-appearance: none;
99
+ appearance: none;
100
+ }
101
+
102
+ /* Placeholder styling */
103
+ .text-input::placeholder {
104
+ color: var(--text-color-default-tertiary);
105
+ }
106
+
107
+ /* Default state - no additional styles needed */
108
+
109
+ /* Hover state */
110
+ .text-input:hover:not(:disabled):not(:focus) {
111
+ border-color: var(--border-color-default);
112
+ background-color: var(--color-background-neutral-primary-hover);
113
+ }
114
+
115
+ /* Focus state */
116
+ .text-input:focus {
117
+ border-color: var(--color-accent);
118
+ background-color: var(--color-background-neutral-primary);
119
+ box-shadow: 0 0 0 3px var(--outline-color-default);
120
+ }
121
+
122
+ /* Error state */
123
+ .text-input.error {
124
+ border-color: var(--color-destructive-base);
125
+ }
126
+
127
+ .text-input.error:focus {
128
+ border-color: var(--color-destructive-base);
129
+ box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.1);
130
+ }
131
+
132
+ /* Disabled state */
133
+ .text-input:disabled {
134
+ background-color: var(--color-background-neutral-disabled);
135
+ border-color: var(--border-color-default);
136
+ color: var(--text-color-default-disabled);
137
+ cursor: not-allowed;
138
+ }
139
+
140
+ .text-input:disabled::placeholder {
141
+ color: var(--text-color-default-disabled);
142
+ }
143
+
144
+ /* Error message */
145
+ .error-message {
146
+ margin-top: 8px;
147
+ font-family: 'Brockmann', system-ui, -apple-system, sans-serif;
148
+ font-size: var(--font-size-sm);
149
+ font-weight: 400;
150
+ line-height: 1.4;
151
+ color: var(--color-destructive-base);
152
+ }
153
+ `],
154
+ template: `
155
+ <div class="text-input-wrapper">
156
+ <input
157
+ [type]="type()"
158
+ [placeholder]="placeholder()"
159
+ [value]="value()"
160
+ [disabled]="disabled()"
161
+ [readonly]="readonly()"
162
+ [required]="required()"
163
+ [autocomplete]="autocomplete() || null"
164
+ [attr.inputmode]="inputmode() || null"
165
+ [class.error]="hasError()"
166
+ [attr.aria-invalid]="hasError()"
167
+ [attr.aria-describedby]="hasError() && errorMessage() ? 'error-' + inputId : null"
168
+ class="text-input"
169
+ (input)="onInput($event)"
170
+ (blur)="onBlur()"
171
+ (focus)="onFocus()"
172
+ [id]="inputId">
173
+
174
+ @if (hasError() && errorMessage()) {
175
+ <div
176
+ class="error-message"
177
+ [id]="'error-' + inputId"
178
+ role="alert">
179
+ {{ errorMessage() }}
180
+ </div>
181
+ }
182
+ </div>
183
+ `
184
+ })
185
+ export class DsTextInputComponent implements ControlValueAccessor {
186
+ // Input properties
187
+ type = input<'text' | 'email' | 'tel' | 'password' | 'url' | 'search'>('text');
188
+ placeholder = input<string>('');
189
+ disabled = input<boolean>(false);
190
+ readonly = input<boolean>(false);
191
+ required = input<boolean>(false);
192
+ hasError = input<boolean>(false);
193
+ errorMessage = input<string>('');
194
+ autocomplete = input<string>('');
195
+ inputmode = input<'text' | 'email' | 'tel' | 'numeric' | 'url' | 'search' | undefined>(undefined);
196
+ autoClearError = input<boolean>(true);
197
+ validator = input<((value: string) => boolean) | null>(null);
198
+
199
+ // Output events
200
+ valueChange = output<string>();
201
+ blur = output<FocusEvent>();
202
+ focus = output<FocusEvent>();
203
+ errorCleared = output<void>();
204
+
205
+ // Internal state
206
+ private _value = signal<string>('');
207
+ value = computed(() => this._value());
208
+
209
+ // Generate unique ID for accessibility
210
+ inputId = `ds-text-input-${Math.random().toString(36).substring(2, 9)}`;
211
+
212
+ // ControlValueAccessor implementation
213
+ private onChange = (value: string) => {};
214
+ private onTouched = () => {};
215
+
216
+ onInput(event: Event): void {
217
+ const target = event.target as HTMLInputElement;
218
+ const newValue = target.value;
219
+ this._value.set(newValue);
220
+ this.onChange(newValue);
221
+ this.valueChange.emit(newValue);
222
+
223
+ // Auto-clear error if input becomes valid
224
+ if (this.autoClearError() && this.hasError()) {
225
+ const isValid = this.validateInput(newValue);
226
+ if (isValid) {
227
+ this.errorCleared.emit();
228
+ }
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Validates the input value based on type or custom validator
234
+ */
235
+ private validateInput(value: string): boolean {
236
+ // Use custom validator if provided
237
+ const customValidator = this.validator();
238
+ if (customValidator) {
239
+ return customValidator(value);
240
+ }
241
+
242
+ // Use built-in validation based on input type
243
+ const inputType = this.type();
244
+ const inputElement = document.createElement('input');
245
+ inputElement.type = inputType;
246
+ inputElement.value = value;
247
+
248
+ // For email type, use HTML5 validation
249
+ if (inputType === 'email') {
250
+ return inputElement.validity.valid;
251
+ }
252
+
253
+ // For required fields, check if value exists
254
+ if (this.required() && !value.trim()) {
255
+ return false;
256
+ }
257
+
258
+ // Default: valid if HTML5 validation passes
259
+ return inputElement.validity.valid;
260
+ }
261
+
262
+ onBlur(): void {
263
+ this.onTouched();
264
+ }
265
+
266
+ onFocus(): void {
267
+ // Focus event can be emitted if needed
268
+ }
269
+
270
+ // ControlValueAccessor methods
271
+ writeValue(value: string): void {
272
+ this._value.set(value || '');
273
+ }
274
+
275
+ registerOnChange(fn: (value: string) => void): void {
276
+ this.onChange = fn;
277
+ }
278
+
279
+ registerOnTouched(fn: () => void): void {
280
+ this.onTouched = fn;
281
+ }
282
+
283
+ setDisabledState(isDisabled: boolean): void {
284
+ // Angular forms will handle this via the disabled input
285
+ }
286
+ }
287
+
@@ -0,0 +1,2 @@
1
+ export * from './ds-text-input';
2
+