@myrjfa/ui 1.1.2 → 2.0.0

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 (176) hide show
  1. package/dist/chat/ChatMain.d.ts +2 -4
  2. package/dist/chat/ChatMain.d.ts.map +1 -1
  3. package/dist/chat/ChatMain.js +4 -5
  4. package/dist/chat/ChatSidebar.d.ts +1 -1
  5. package/dist/chat/ChatSidebar.d.ts.map +1 -1
  6. package/dist/chat/ConversationList.d.ts +1 -1
  7. package/dist/chat/ConversationList.d.ts.map +1 -1
  8. package/dist/chat/ConversationList.js +1 -1
  9. package/dist/chat/ExpensePanel.js +1 -1
  10. package/dist/chat/MessageBubble.d.ts +1 -1
  11. package/dist/chat/MessageBubble.d.ts.map +1 -1
  12. package/dist/chat/MessageBubble.js +1 -1
  13. package/dist/chat/MessageInput.d.ts +1 -1
  14. package/dist/chat/MessageInput.d.ts.map +1 -1
  15. package/dist/chat/MessageInput.js +1 -1
  16. package/dist/chat/MilestonePanel.js +1 -1
  17. package/dist/chat/components/ParticipantSelector.d.ts +1 -1
  18. package/dist/chat/components/ParticipantSelector.d.ts.map +1 -1
  19. package/dist/chat/components/SplitInputs.d.ts +1 -1
  20. package/dist/chat/components/SplitInputs.d.ts.map +1 -1
  21. package/dist/chat/components/SplitTypeSelector.d.ts +1 -1
  22. package/dist/chat/components/SplitTypeSelector.d.ts.map +1 -1
  23. package/dist/chat/dialogs/AddMemberDialog.d.ts +1 -1
  24. package/dist/chat/dialogs/AddMemberDialog.d.ts.map +1 -1
  25. package/dist/chat/dialogs/AddMemberDialog.js +1 -1
  26. package/dist/chat/dialogs/ExpenseAnalyticsDialog.js +1 -1
  27. package/dist/chat/dialogs/GroupInfoDialog.d.ts +1 -1
  28. package/dist/chat/dialogs/GroupInfoDialog.d.ts.map +1 -1
  29. package/dist/chat/dialogs/GroupInfoDialog.js +1 -1
  30. package/dist/chat/dialogs/GroupInfoViewDialog.d.ts +1 -1
  31. package/dist/chat/dialogs/GroupInfoViewDialog.d.ts.map +1 -1
  32. package/dist/chat/dialogs/NewChatDialog.js +2 -2
  33. package/dist/chat/dialogs/NewGroupDialog.js +2 -2
  34. package/dist/chat/dialogs/ParticipantsDialog.d.ts +1 -1
  35. package/dist/chat/dialogs/ParticipantsDialog.d.ts.map +1 -1
  36. package/dist/chat/dialogs/ParticipantsDialog.js +1 -1
  37. package/dist/dashboard/action-buttons.d.ts.map +1 -1
  38. package/dist/dashboard/action-buttons.js +7 -1
  39. package/dist/dashboard/app-sidebar.d.ts.map +1 -1
  40. package/dist/dashboard/app-sidebar.js +9 -4
  41. package/dist/dashboard/card-header.d.ts +1 -1
  42. package/dist/dashboard/card-header.d.ts.map +1 -1
  43. package/dist/dashboard/card-header.js +1 -1
  44. package/dist/dashboard/dashboard-components.js +1 -1
  45. package/dist/dashboard/data-table.d.ts +1 -2
  46. package/dist/dashboard/data-table.d.ts.map +1 -1
  47. package/dist/dashboard/data-table.js +11 -11
  48. package/dist/dashboard/table.js +1 -1
  49. package/dist/header/mobile-nav-item.js +1 -1
  50. package/dist/header/nav-item.js +1 -1
  51. package/dist/index.d.ts +3 -7
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +4 -8
  54. package/dist/main/footer.d.ts +1 -0
  55. package/dist/main/footer.d.ts.map +1 -1
  56. package/dist/main/footer.js +35 -16
  57. package/dist/main/navbar.d.ts +2 -2
  58. package/dist/main/navbar.d.ts.map +1 -1
  59. package/dist/main/navbar.js +24 -18
  60. package/dist/main/paginationBar.d.ts +1 -1
  61. package/dist/main/paginationBar.d.ts.map +1 -1
  62. package/dist/main/paginationBar.js +1 -1
  63. package/dist/main/portfolio/portfolioItem.d.ts +1 -1
  64. package/dist/main/portfolio/portfolioItem.d.ts.map +1 -1
  65. package/dist/main/portfolio/portfolioPopup.d.ts +1 -1
  66. package/dist/main/portfolio/portfolioPopup.d.ts.map +1 -1
  67. package/dist/main/portfolio/portfolioPopup.js +1 -1
  68. package/dist/main/review-page.d.ts +2 -2
  69. package/dist/main/review-page.d.ts.map +1 -1
  70. package/dist/main/review-page.js +10 -50
  71. package/dist/misc/accordion.js +1 -1
  72. package/dist/misc/alert-dialog.js +1 -1
  73. package/dist/misc/alert.js +1 -1
  74. package/dist/misc/alertdialogclaude.js +1 -1
  75. package/dist/misc/avatar.js +1 -1
  76. package/dist/misc/badge.js +1 -1
  77. package/dist/misc/button.d.ts +9 -9
  78. package/dist/misc/button.js +1 -1
  79. package/dist/misc/card.js +1 -1
  80. package/dist/misc/carousel.js +1 -1
  81. package/dist/misc/checkbox.js +1 -1
  82. package/dist/misc/command.js +2 -2
  83. package/dist/misc/datepicker.d.ts.map +1 -1
  84. package/dist/misc/datepicker.js +22 -4
  85. package/dist/misc/dialog.js +1 -1
  86. package/dist/misc/dropdown-menu.js +1 -1
  87. package/dist/misc/faq.d.ts +2 -2
  88. package/dist/misc/faq.d.ts.map +1 -1
  89. package/dist/misc/faq.js +1 -1
  90. package/dist/misc/file-preview.d.ts +1 -1
  91. package/dist/misc/file-preview.d.ts.map +1 -1
  92. package/dist/misc/file-preview.js +1 -1
  93. package/dist/misc/filter.d.ts +1 -1
  94. package/dist/misc/filter.d.ts.map +1 -1
  95. package/dist/misc/filter.js +3 -3
  96. package/dist/misc/filterBar.d.ts +2 -2
  97. package/dist/misc/filterBar.d.ts.map +1 -1
  98. package/dist/misc/filterBar.js +3 -3
  99. package/dist/misc/form.js +1 -1
  100. package/dist/misc/input.d.ts.map +1 -1
  101. package/dist/misc/input.js +2 -2
  102. package/dist/misc/label.js +1 -1
  103. package/dist/misc/line-carousel.d.ts +1 -1
  104. package/dist/misc/line-carousel.d.ts.map +1 -1
  105. package/dist/misc/line-carousel.js +1 -1
  106. package/dist/misc/location/leafletMap.d.ts +8 -0
  107. package/dist/misc/location/leafletMap.d.ts.map +1 -0
  108. package/dist/misc/location/leafletMap.js +62 -0
  109. package/dist/misc/location/locationPicker.d.ts +5 -0
  110. package/dist/misc/location/locationPicker.d.ts.map +1 -0
  111. package/dist/misc/location/locationPicker.js +94 -0
  112. package/dist/misc/multiImageUploader.d.ts +1 -2
  113. package/dist/misc/multiImageUploader.d.ts.map +1 -1
  114. package/dist/misc/multiImageUploader.js +2 -26
  115. package/dist/misc/newsLetter.d.ts +1 -1
  116. package/dist/misc/newsLetter.d.ts.map +1 -1
  117. package/dist/misc/newsLetter.js +2 -2
  118. package/dist/misc/notification.js +2 -2
  119. package/dist/misc/pagination.js +1 -1
  120. package/dist/misc/password-eye.d.ts +2 -1
  121. package/dist/misc/password-eye.d.ts.map +1 -1
  122. package/dist/misc/password-eye.js +2 -1
  123. package/dist/misc/popover.d.ts.map +1 -1
  124. package/dist/misc/popover.js +2 -2
  125. package/dist/misc/progress.js +1 -1
  126. package/dist/misc/rental-tile.d.ts +6 -0
  127. package/dist/misc/rental-tile.d.ts.map +1 -0
  128. package/dist/misc/rental-tile.js +23 -0
  129. package/dist/misc/scroll-area.js +1 -1
  130. package/dist/misc/searchBar.d.ts +1 -1
  131. package/dist/misc/searchBar.d.ts.map +1 -1
  132. package/dist/misc/searchBar.js +1 -1
  133. package/dist/misc/select.d.ts.map +1 -1
  134. package/dist/misc/select.js +4 -4
  135. package/dist/misc/separator.js +1 -1
  136. package/dist/misc/share.d.ts +1 -1
  137. package/dist/misc/share.d.ts.map +1 -1
  138. package/dist/misc/share.js +1 -1
  139. package/dist/misc/sheet.js +1 -1
  140. package/dist/misc/sidebar.js +2 -2
  141. package/dist/misc/skeleton.d.ts +1 -36
  142. package/dist/misc/skeleton.d.ts.map +1 -1
  143. package/dist/misc/skeleton.js +3 -40
  144. package/dist/misc/slider.js +1 -1
  145. package/dist/misc/stateCard.d.ts +2 -2
  146. package/dist/misc/stateCard.d.ts.map +1 -1
  147. package/dist/misc/stateCard.js +2 -2
  148. package/dist/misc/switch.js +1 -1
  149. package/dist/misc/tabs.js +1 -1
  150. package/dist/misc/textarea.js +1 -1
  151. package/dist/misc/tile.d.ts +2 -1
  152. package/dist/misc/tile.d.ts.map +1 -1
  153. package/dist/misc/tile.js +12 -8
  154. package/dist/misc/tooltip.js +1 -1
  155. package/dist/profile/socialLink.d.ts +1 -1
  156. package/dist/profile/socialLink.d.ts.map +1 -1
  157. package/dist/profile/socialLink.js +19 -49
  158. package/package.json +11 -5
  159. package/dist/misc/document-upload-button.d.ts +0 -12
  160. package/dist/misc/document-upload-button.d.ts.map +0 -1
  161. package/dist/misc/document-upload-button.js +0 -27
  162. package/dist/misc/empty-state.d.ts +0 -10
  163. package/dist/misc/empty-state.d.ts.map +0 -1
  164. package/dist/misc/empty-state.js +0 -5
  165. package/dist/misc/image-compressor.d.ts +0 -29
  166. package/dist/misc/image-compressor.d.ts.map +0 -1
  167. package/dist/misc/image-compressor.js +0 -70
  168. package/dist/misc/image-cropper.d.ts +0 -10
  169. package/dist/misc/image-cropper.d.ts.map +0 -1
  170. package/dist/misc/image-cropper.js +0 -44
  171. package/dist/misc/pwa-install-button.d.ts +0 -9
  172. package/dist/misc/pwa-install-button.d.ts.map +0 -1
  173. package/dist/misc/pwa-install-button.js +0 -158
  174. package/dist/misc/whatsapp-help.d.ts +0 -9
  175. package/dist/misc/whatsapp-help.d.ts.map +0 -1
  176. package/dist/misc/whatsapp-help.js +0 -13
@@ -3,60 +3,30 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { Input } from "../misc/input";
5
5
  import { Button } from "../misc/button";
6
- import { GithubIcon, InstagramIcon, StoreIcon, YoutubeIcon } from "lucide-react";
7
- import { normalizeSocialUrl } from "@myrjfa/state/dist/lib/utils/socialMediaUrl";
8
- import { FacebookIcon, LinkedinIcon, TwitterIcon } from "react-share";
6
+ import { InstagramIcon, YoutubeIcon, LinkedinIcon, StoreIcon } from "lucide-react";
7
+ import { useRouter } from "next/navigation";
9
8
  const platformConfig = {
10
- instagram: { label: "Instagram", icon: InstagramIcon, color: "hover:text-pink-600", hint: "Enter username or profile URL" },
11
- youtube: { label: "YouTube", icon: YoutubeIcon, color: "hover:text-red-600", hint: "Enter @handle or profile URL" },
12
- twitter: { label: "Twitter", icon: TwitterIcon, color: "hover:text-blue-600", hint: "Enter @handle or profile URL" },
13
- facebook: { label: "Facebook", icon: FacebookIcon, color: "hover:text-blue-600", hint: "Enter @handle or profile URL" },
14
- github: { label: "GitHub", icon: GithubIcon, color: "hover:text-blue-600", hint: "Enter @handle or profile URL" },
15
- linkedin: { label: "LinkedIn", icon: LinkedinIcon, color: "hover:text-blue-600", hint: "Enter username or profile URL" },
16
- googleBusiness: { label: "Google Business", icon: StoreIcon, color: "hover:text-green-600", hint: "Enter full Google Business URL" },
9
+ instagram: { label: "Instagram", icon: InstagramIcon, color: "hover:text-pink-600" },
10
+ youtube: { label: "YouTube", icon: YoutubeIcon, color: "hover:text-red-600" },
11
+ linkedin: { label: "LinkedIn", icon: LinkedinIcon, color: "hover:text-blue-600" },
12
+ googleBusiness: { label: "Google Business", icon: StoreIcon, color: "hover:text-green-600" },
17
13
  };
18
14
  export function SocialLinkField({ platform, url, isCurrentUser, updateUserSocialLink, }) {
19
- const [input, setInput] = useState(url !== null && url !== void 0 ? url : "");
20
- const [urlError, setUrlError] = useState(null);
15
+ const [input, setInput] = useState(url);
21
16
  const [loading, setLoading] = useState(false);
22
- const { label, icon: Icon, color, hint } = platformConfig[platform];
23
- // #13: Validate and normalize the URL when focus leaves the input
24
- const handleBlur = () => {
25
- if (!input.trim() || platform === "googleBusiness")
26
- return;
27
- const { url: normalized, error } = normalizeSocialUrl(platform, input);
28
- setUrlError(error);
29
- if (!error)
30
- setInput(normalized); // auto-correct the field to canonical URL
31
- };
32
- const handleSave = async () => {
33
- if (!input.trim() || !updateUserSocialLink)
34
- return;
35
- // Final normalization before saving
36
- let finalUrl = input;
37
- if (platform !== "googleBusiness") {
38
- const { url: normalized, error } = normalizeSocialUrl(platform, input);
39
- if (error) {
40
- setUrlError(error);
41
- return;
42
- }
43
- finalUrl = normalized;
44
- setInput(normalized);
45
- }
46
- try {
47
- setLoading(true);
48
- await updateUserSocialLink(platform, finalUrl);
49
- setUrlError(null);
50
- }
51
- finally {
52
- setLoading(false);
53
- }
54
- };
17
+ const router = useRouter();
18
+ const { label, icon: Icon, color } = platformConfig[platform];
55
19
  if (isCurrentUser) {
56
- return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Icon, { className: `h-5 w-5 shrink-0 text-gray-400 ${color}` }), _jsx(Input, { type: "text", placeholder: hint, value: input, onChange: (e) => {
57
- setInput(e.target.value);
58
- setUrlError(null);
59
- }, onBlur: handleBlur, className: urlError ? "border-red-400 focus:ring-red-400" : "" }), _jsx(Button, { disabled: loading || !input.trim(), onClick: handleSave, children: loading ? "Adding..." : "Add" })] }), urlError && (_jsx("p", { className: "text-xs text-red-500 ml-6", children: urlError }))] }));
20
+ return (_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Input, { type: "url", placeholder: `${label} profile url`, value: input, onChange: (e) => setInput(e.target.value) }), updateUserSocialLink &&
21
+ _jsx(Button, { disabled: loading || !input, onClick: async () => {
22
+ try {
23
+ setLoading(true);
24
+ await updateUserSocialLink(platform, input);
25
+ }
26
+ finally {
27
+ setLoading(false);
28
+ }
29
+ }, children: "Add" })] }));
60
30
  }
61
31
  else if (url) {
62
32
  return (_jsx("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: `text-gray-500 ${color} flex flex-row gap-1`, children: _jsx(Icon, { className: "h-5 w-5" }) }));
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "@myrjfa/ui",
3
- "version": "1.1.2",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
+ "exports": {
6
+ ".": "./dist/index.js",
7
+ "./*": "./*.js"
8
+ },
5
9
  "description": "Shared UI components for my apps",
6
10
  "main": "dist/index.js",
7
11
  "types": "dist/index.d.ts",
8
- "sideEffects": false,
9
12
  "files": [
10
13
  "dist"
11
14
  ],
@@ -38,9 +41,11 @@
38
41
  "cmdk": "1.1.1",
39
42
  "embla-carousel-react": "^8.5.2",
40
43
  "emoji-picker-react": "^4.18.0",
41
- "react-easy-crop": "^5.5.6",
44
+ "leaflet": "1.9.4",
45
+ "pnpm": "^10.14.0",
42
46
  "react-hook-form": "^7.54.2",
43
47
  "react-icons": "^5.5.0",
48
+ "react-leaflet": "^5.0.0",
44
49
  "react-share": "^5.2.2"
45
50
  },
46
51
  "peerDependencies": {
@@ -50,11 +55,12 @@
50
55
  "lucide-react": "*",
51
56
  "next": "*",
52
57
  "react": "*",
53
- "react-dom": "*",
58
+ "react-dom": "",
54
59
  "zod": "*"
55
60
  },
56
61
  "devDependencies": {
57
- "@myrjfa/state": "1.1.2",
62
+ "@myrjfa/state": "2.0.0",
63
+ "@types/leaflet": "1.9.21",
58
64
  "@types/react": "19.0.10",
59
65
  "@types/react-dom": "^19.0.4",
60
66
  "date-fns": "^3.0.0",
@@ -1,12 +0,0 @@
1
- type UploadType = "image" | "document" | "both";
2
- interface FileUploadProps {
3
- label?: string;
4
- required?: boolean;
5
- type?: UploadType;
6
- value?: File | null;
7
- onChange: (file: File | null) => void;
8
- className?: string;
9
- }
10
- export declare function FileUpload({ label, required, type, value, onChange, className, }: FileUploadProps): import("react/jsx-runtime").JSX.Element;
11
- export {};
12
- //# sourceMappingURL=document-upload-button.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"document-upload-button.d.ts","sourceRoot":"","sources":["../../src/misc/document-upload-button.tsx"],"names":[],"mappings":"AAMA,KAAK,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;AAEhD,UAAU,eAAe;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,UAAU,CAAC,EACvB,KAAqB,EACrB,QAAQ,EACR,IAAa,EACb,KAAK,EACL,QAAQ,EACR,SAAS,GACZ,EAAE,eAAe,2CAmEjB"}
@@ -1,27 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useRef } from "react";
3
- import { cn } from "@myrjfa/state/dist/lib/utils";
4
- import { FileUp, ImageIcon, FileText } from "lucide-react";
5
- import { Input } from "./input";
6
- import { Label } from "./label";
7
- export function FileUpload({ label = "Upload File", required, type = "both", value, onChange, className, }) {
8
- const inputRef = useRef(null);
9
- const acceptMap = {
10
- image: "image/*",
11
- document: "application/pdf",
12
- both: "application/pdf,image/*",
13
- };
14
- const helperTextMap = {
15
- image: "Image only",
16
- document: "PDF only",
17
- both: "PDF or image",
18
- };
19
- const Icon = type === "image"
20
- ? ImageIcon
21
- : type === "document"
22
- ? FileText
23
- : FileUp;
24
- return (_jsxs("div", { className: cn("w-full", className), children: [_jsxs(Label, { className: "text-sm font-medium", children: [label, " ", required && _jsx("span", { className: "text-destructive", children: "*" }), _jsxs("span", { className: "text-xs text-muted-foreground ml-2", children: ["(", helperTextMap[type], ")"] })] }), _jsxs("div", { onClick: () => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, className: cn("mt-2 border-2 border-dashed rounded-lg cursor-pointer transition-all", "flex flex-col items-center justify-center text-center", "hover:border-primary/50",
25
- // 📱 Responsive sizing
26
- "p-3"), children: [_jsx(Icon, { className: "w-5 h-5 sm:w-6 sm:h-6 text-muted-foreground mb-1" }), value ? (_jsx("p", { className: "text-xs sm:text-sm text-primary font-medium break-all", children: value.name })) : (_jsx("p", { className: "text-xs sm:text-sm text-muted-foreground", children: "Tap or click to upload" })), _jsx(Input, { ref: inputRef, type: "file", accept: acceptMap[type], className: "hidden", onChange: (e) => { var _a, _b; return onChange((_b = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : null); } })] })] }));
27
- }
@@ -1,10 +0,0 @@
1
- import React from 'react';
2
- interface EmptyStateProps {
3
- title?: string;
4
- description?: string;
5
- icon?: React.ReactNode;
6
- action?: React.ReactNode;
7
- }
8
- export declare function EmptyState({ title, description, icon, action, }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
9
- export {};
10
- //# sourceMappingURL=empty-state.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"empty-state.d.ts","sourceRoot":"","sources":["../../src/misc/empty-state.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,UAAU,eAAe;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,wBAAgB,UAAU,CAAC,EACvB,KAA0B,EAC1B,WAAW,EACX,IAAI,EACJ,MAAM,GACT,EAAE,eAAe,2CAajB"}
@@ -1,5 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Inbox } from 'lucide-react';
3
- export function EmptyState({ title = 'Nothing here yet', description, icon, action, }) {
4
- return (_jsxs("div", { className: "flex flex-col items-center justify-center py-16 px-4 text-center gap-3", children: [_jsx("div", { className: "text-muted-foreground", children: icon !== null && icon !== void 0 ? icon : _jsx(Inbox, { className: "w-12 h-12 mx-auto mb-2 opacity-40" }) }), _jsx("h3", { className: "text-lg font-semibold text-foreground", children: title }), description && (_jsx("p", { className: "text-sm text-muted-foreground max-w-xs", children: description })), action && _jsx("div", { className: "mt-2", children: action })] }));
5
- }
@@ -1,29 +0,0 @@
1
- export interface CompressOptions {
2
- quality?: number;
3
- maxWidth?: number;
4
- maxHeight?: number;
5
- }
6
- /**
7
- * Compress an image File using Canvas API.
8
- * Returns a new File object with the same name but compressed content.
9
- */
10
- export declare function compressImage(file: File, options?: CompressOptions): Promise<File>;
11
- export interface ImageCompressorProps {
12
- enableCompression?: boolean;
13
- quality?: number;
14
- maxWidth?: number;
15
- maxHeight?: number;
16
- accept?: string;
17
- multiple?: boolean;
18
- onChange: (files: File[]) => void;
19
- children?: React.ReactNode;
20
- className?: string;
21
- id?: string;
22
- }
23
- /**
24
- * #26 — Image Compressor Input Component
25
- * Drop-in replacement for a file input that optionally compresses images
26
- * before passing them to the onChange handler.
27
- */
28
- export declare function ImageCompressorInput({ enableCompression, quality, maxWidth, maxHeight, accept, multiple, onChange, children, className, id, }: ImageCompressorProps): import("react/jsx-runtime").JSX.Element;
29
- //# sourceMappingURL=image-compressor.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"image-compressor.d.ts","sourceRoot":"","sources":["../../src/misc/image-compressor.tsx"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAC/B,IAAI,EAAE,IAAI,EACV,OAAO,GAAE,eAAoB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAkDf;AAED,MAAM,WAAW,oBAAoB;IACjC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,EACjC,iBAAwB,EACxB,OAAa,EACb,QAAe,EACf,SAAgB,EAChB,MAAoD,EACpD,QAAgB,EAChB,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,EAAE,GACL,EAAE,oBAAoB,2CAmCtB"}
@@ -1,70 +0,0 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useRef, useCallback } from 'react';
4
- /**
5
- * Compress an image File using Canvas API.
6
- * Returns a new File object with the same name but compressed content.
7
- */
8
- export async function compressImage(file, options = {}) {
9
- const { quality = 0.8, maxWidth = 1920, maxHeight = 1920 } = options;
10
- return new Promise((resolve, reject) => {
11
- const img = new Image();
12
- const url = URL.createObjectURL(file);
13
- img.onload = () => {
14
- URL.revokeObjectURL(url);
15
- let { width, height } = img;
16
- if (width > maxWidth) {
17
- height = Math.round((height * maxWidth) / width);
18
- width = maxWidth;
19
- }
20
- if (height > maxHeight) {
21
- width = Math.round((width * maxHeight) / height);
22
- height = maxHeight;
23
- }
24
- const canvas = document.createElement('canvas');
25
- canvas.width = width;
26
- canvas.height = height;
27
- const ctx = canvas.getContext('2d');
28
- if (!ctx) {
29
- resolve(file); // fallback: return original
30
- return;
31
- }
32
- ctx.drawImage(img, 0, 0, width, height);
33
- canvas.toBlob((blob) => {
34
- if (!blob) {
35
- resolve(file); // fallback
36
- return;
37
- }
38
- resolve(new File([blob], file.name, { type: 'image/webp', lastModified: Date.now() }));
39
- }, 'image/webp', quality);
40
- };
41
- img.onerror = () => {
42
- URL.revokeObjectURL(url);
43
- reject(new Error(`Failed to load image: ${file.name}`));
44
- };
45
- img.src = url;
46
- });
47
- }
48
- /**
49
- * #26 — Image Compressor Input Component
50
- * Drop-in replacement for a file input that optionally compresses images
51
- * before passing them to the onChange handler.
52
- */
53
- export function ImageCompressorInput({ enableCompression = true, quality = 0.8, maxWidth = 1920, maxHeight = 1920, accept = 'image/jpeg,image/jpg,image/png,image/webp', multiple = false, onChange, children, className, id, }) {
54
- const inputRef = useRef(null);
55
- const handleChange = useCallback(async (e) => {
56
- const rawFiles = Array.from(e.target.files || []);
57
- if (!rawFiles.length)
58
- return;
59
- if (!enableCompression) {
60
- onChange(rawFiles);
61
- return;
62
- }
63
- const compressed = await Promise.all(rawFiles.map(f => compressImage(f, { quality, maxWidth, maxHeight }).catch(() => f)));
64
- onChange(compressed);
65
- // Reset input so the same file can be re-selected
66
- if (inputRef.current)
67
- inputRef.current.value = '';
68
- }, [enableCompression, quality, maxWidth, maxHeight, onChange]);
69
- return (_jsxs("label", { className: className, style: { cursor: 'pointer' }, children: [children, _jsx("input", { ref: inputRef, id: id, type: "file", accept: accept, multiple: multiple, onChange: handleChange, style: { display: 'none' } })] }));
70
- }
@@ -1,10 +0,0 @@
1
- import React from "react";
2
- interface ImageCropperProps {
3
- image: string;
4
- onCancel: () => void;
5
- onCropComplete: (croppedImage: Blob) => void;
6
- open: boolean;
7
- }
8
- declare const ImageCropper: React.FC<ImageCropperProps>;
9
- export default ImageCropper;
10
- //# sourceMappingURL=image-cropper.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"image-cropper.d.ts","sourceRoot":"","sources":["../../src/misc/image-cropper.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAMrD,UAAU,iBAAiB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,cAAc,EAAE,CAAC,YAAY,EAAE,IAAI,KAAK,IAAI,CAAC;IAC7C,IAAI,EAAE,OAAO,CAAC;CACjB;AAED,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgG7C,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -1,44 +0,0 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useCallback } from "react";
4
- import Cropper from "react-easy-crop";
5
- import { Button } from "./button";
6
- import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "./dialog";
7
- import { Slider } from "./slider";
8
- const ImageCropper = ({ image, onCancel, onCropComplete, open }) => {
9
- const [crop, setCrop] = useState({ x: 0, y: 0 });
10
- const [zoom, setZoom] = useState(1);
11
- const [croppedAreaPixels, setCroppedAreaPixels] = useState(null);
12
- const onCropChange = (crop) => {
13
- setCrop(crop);
14
- };
15
- const onZoomChange = (zoom) => {
16
- setZoom(zoom);
17
- };
18
- const onCropCompleteCallback = useCallback((_, croppedAreaPixels) => {
19
- setCroppedAreaPixels(croppedAreaPixels);
20
- }, []);
21
- const createCroppedImage = async () => {
22
- if (!croppedAreaPixels || !image)
23
- return;
24
- const img = new Image();
25
- img.src = image;
26
- await new Promise((resolve) => {
27
- img.onload = resolve;
28
- });
29
- const canvas = document.createElement("canvas");
30
- const ctx = canvas.getContext("2d");
31
- if (!ctx)
32
- return;
33
- canvas.width = croppedAreaPixels.width;
34
- canvas.height = croppedAreaPixels.height;
35
- ctx.drawImage(img, croppedAreaPixels.x, croppedAreaPixels.y, croppedAreaPixels.width, croppedAreaPixels.height, 0, 0, croppedAreaPixels.width, croppedAreaPixels.height);
36
- canvas.toBlob((blob) => {
37
- if (blob) {
38
- onCropComplete(blob);
39
- }
40
- }, "image/jpeg");
41
- };
42
- return (_jsx(Dialog, { open: open, onOpenChange: (isOpen) => !isOpen && onCancel(), children: _jsxs(DialogContent, { className: "sm:max-w-[500px] z-[300]", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Crop Profile Picture" }) }), _jsx("div", { className: "relative h-[300px] w-full bg-gray-100 rounded-md overflow-hidden", children: _jsx(Cropper, { image: image, crop: crop, zoom: zoom, aspect: 1, cropShape: "round", showGrid: false, onCropChange: onCropChange, onCropComplete: onCropCompleteCallback, onZoomChange: onZoomChange }) }), _jsx("div", { className: "py-4", children: _jsxs("div", { className: "flex items-center gap-4", children: [_jsx("span", { className: "text-sm font-medium", children: "Zoom" }), _jsx(Slider, { value: [zoom], min: 1, max: 5, step: 0.1, onValueChange: (values) => { var _a; return setZoom((_a = values[0]) !== null && _a !== void 0 ? _a : 1); }, className: "flex-1" })] }) }), _jsxs(DialogFooter, { className: "flex gap-2", children: [_jsx(Button, { variant: "outline", onClick: onCancel, children: "Cancel" }), _jsx(Button, { onClick: createCroppedImage, children: "Set Profile Picture" })] })] }) }));
43
- };
44
- export default ImageCropper;
@@ -1,9 +0,0 @@
1
- /**
2
- * #9 — Floating PWA Install Button
3
- * Premium banner aligned with Trippeaze brand.
4
- * - Only shows on mobile (beforeinstallprompt)
5
- * - Auto-hides after 10 s
6
- * - Animated entrance / exit
7
- */
8
- export declare function PwaInstallButton(): import("react/jsx-runtime").JSX.Element | null;
9
- //# sourceMappingURL=pwa-install-button.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pwa-install-button.d.ts","sourceRoot":"","sources":["../../src/misc/pwa-install-button.tsx"],"names":[],"mappings":"AAKA;;;;;;GAMG;AACH,wBAAgB,gBAAgB,mDA0M/B"}
@@ -1,158 +0,0 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useEffect, useRef, useState } from 'react';
4
- import Image from 'next/image';
5
- /**
6
- * #9 — Floating PWA Install Button
7
- * Premium banner aligned with Trippeaze brand.
8
- * - Only shows on mobile (beforeinstallprompt)
9
- * - Auto-hides after 10 s
10
- * - Animated entrance / exit
11
- */
12
- export function PwaInstallButton() {
13
- const [show, setShow] = useState(false);
14
- const [hiding, setHiding] = useState(false);
15
- const promptRef = useRef(null);
16
- const timerRef = useRef(null);
17
- const dismiss = () => {
18
- setHiding(true);
19
- setTimeout(() => setShow(false), 350);
20
- };
21
- useEffect(() => {
22
- const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
23
- if (!isMobile)
24
- return;
25
- const handler = (e) => {
26
- e.preventDefault();
27
- promptRef.current = e;
28
- setShow(true);
29
- setHiding(false);
30
- timerRef.current = setTimeout(dismiss, 10000);
31
- };
32
- window.addEventListener('beforeinstallprompt', handler);
33
- return () => {
34
- window.removeEventListener('beforeinstallprompt', handler);
35
- if (timerRef.current)
36
- clearTimeout(timerRef.current);
37
- };
38
- }, []);
39
- const handleInstall = async () => {
40
- if (!promptRef.current)
41
- return;
42
- if (timerRef.current)
43
- clearTimeout(timerRef.current);
44
- promptRef.current.prompt();
45
- const { outcome } = await promptRef.current.userChoice;
46
- if (outcome === 'accepted')
47
- dismiss();
48
- };
49
- if (!show)
50
- return null;
51
- return (_jsxs(_Fragment, { children: [_jsx("style", { children: `
52
- @keyframes tpz-slide-up {
53
- from { opacity: 0; transform: translateX(-50%) translateY(20px); }
54
- to { opacity: 1; transform: translateX(-50%) translateY(0); }
55
- }
56
- @keyframes tpz-slide-down {
57
- from { opacity: 1; transform: translateX(-50%) translateY(0); }
58
- to { opacity: 0; transform: translateX(-50%) translateY(20px); }
59
- }
60
- @keyframes tpz-orbit {
61
- from { transform: rotate(0deg) translateX(22px) rotate(0deg); }
62
- to { transform: rotate(360deg) translateX(22px) rotate(-360deg); }
63
- }
64
- @keyframes tpz-shimmer {
65
- 0% { background-position: -200% center; }
66
- 100% { background-position: 200% center; }
67
- }
68
- ` }), _jsxs("div", { style: {
69
- position: 'fixed',
70
- bottom: '88px',
71
- left: '50%',
72
- transform: 'translateX(-50%)',
73
- zIndex: 9999,
74
- animation: hiding
75
- ? 'tpz-slide-down 0.35s cubic-bezier(.4,0,.2,1) forwards'
76
- : 'tpz-slide-up 0.4s cubic-bezier(.16,1,.3,1) forwards',
77
- // Prevent layout shift during animation
78
- willChange: 'transform, opacity',
79
- }, children: [_jsx("div", { style: {
80
- position: 'absolute',
81
- inset: '-3px',
82
- borderRadius: '9999px',
83
- background: 'linear-gradient(135deg, #f97316, #fb923c, #22d3ee, #0891b2)',
84
- opacity: 0.3,
85
- filter: 'blur(8px)',
86
- zIndex: 0,
87
- } }), _jsxs("button", { onClick: handleInstall, style: {
88
- position: 'relative',
89
- zIndex: 1,
90
- display: 'flex',
91
- alignItems: 'center',
92
- gap: '10px',
93
- padding: '10px 18px 10px 12px',
94
- borderRadius: '9999px',
95
- border: '1px solid rgba(0,0,0,0.06)',
96
- background: 'linear-gradient(135deg, #ffffff 0%, #f8fafc 100%)',
97
- boxShadow: '0 12px 32px rgba(0,0,0,0.12), inset 0 1px 0 rgba(255,255,255,0.9)',
98
- cursor: 'pointer',
99
- whiteSpace: 'nowrap',
100
- backdropFilter: 'blur(8px)',
101
- }, children: [_jsx("div", { style: {
102
- width: '36px',
103
- height: '36px',
104
- borderRadius: '50%',
105
- overflow: 'hidden',
106
- flexShrink: 0,
107
- boxShadow: '0 2px 8px rgba(249,115,22,0.2)',
108
- }, children: _jsx(Image, { src: "/images/logoicon.webp", alt: "Trippeaze", width: 36, height: 36, style: { objectFit: 'cover' } }) }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: '1px' }, children: [_jsx("span", { style: {
109
- fontSize: '11px',
110
- fontWeight: 500,
111
- letterSpacing: '0.08em',
112
- textTransform: 'uppercase',
113
- color: '#64748b',
114
- lineHeight: 1,
115
- }, children: "Get the app" }), _jsx("span", { style: {
116
- fontSize: '15px',
117
- fontWeight: 700,
118
- letterSpacing: '-0.01em',
119
- lineHeight: 1.2,
120
- // Shimmer text using logo colors
121
- background: 'linear-gradient(90deg, #fb923c, #fbbf24, #22d3ee, #fb923c)',
122
- backgroundSize: '200% auto',
123
- WebkitBackgroundClip: 'text',
124
- WebkitTextFillColor: 'transparent',
125
- backgroundClip: 'text',
126
- animation: 'tpz-shimmer 3s linear infinite',
127
- }, children: "Install Trippeaze" })] }), _jsx("div", { style: {
128
- marginLeft: '4px',
129
- width: '28px',
130
- height: '28px',
131
- borderRadius: '50%',
132
- background: 'linear-gradient(135deg, #f97316, #dc2626)',
133
- display: 'flex',
134
- alignItems: 'center',
135
- justifyContent: 'center',
136
- flexShrink: 0,
137
- boxShadow: '0 2px 8px rgba(249,115,22,0.5)',
138
- }, children: _jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", children: _jsx("path", { d: "M7 2v7M4 6.5l3 3 3-3M2.5 11h9", stroke: "white", strokeWidth: "1.7", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] }), _jsx("button", { onClick: (e) => { e.stopPropagation(); dismiss(); }, "aria-label": "Dismiss", style: {
139
- position: 'absolute',
140
- top: '-6px',
141
- right: '-6px',
142
- zIndex: 2,
143
- width: '20px',
144
- height: '20px',
145
- borderRadius: '50%',
146
- background: '#ffffff',
147
- border: '1px solid rgba(0,0,0,0.1)',
148
- color: '#64748b',
149
- fontSize: '11px',
150
- lineHeight: '20px',
151
- textAlign: 'center',
152
- cursor: 'pointer',
153
- display: 'flex',
154
- alignItems: 'center',
155
- justifyContent: 'center',
156
- boxShadow: '0 2px 6px rgba(0,0,0,0.1)',
157
- }, children: "\u2715" })] })] }));
158
- }
@@ -1,9 +0,0 @@
1
- /**
2
- * #21 — Floating WhatsApp Help Button
3
- * Fixed bottom-right pill button that opens a WhatsApp chat with support.
4
- * Matches the "Need help? Chat with us" pill + green icon design.
5
- */
6
- export declare function WhatsAppHelpButton({ message }: {
7
- message: string;
8
- }): import("react/jsx-runtime").JSX.Element;
9
- //# sourceMappingURL=whatsapp-help.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"whatsapp-help.d.ts","sourceRoot":"","sources":["../../src/misc/whatsapp-help.tsx"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,2CAyClE"}
@@ -1,13 +0,0 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { WhatsappIcon } from 'react-share';
4
- import Link from 'next/link';
5
- /**
6
- * #21 — Floating WhatsApp Help Button
7
- * Fixed bottom-right pill button that opens a WhatsApp chat with support.
8
- * Matches the "Need help? Chat with us" pill + green icon design.
9
- */
10
- export function WhatsAppHelpButton({ message }) {
11
- const whatsappUrl = `https://wa.me/918482944029?text=${encodeURIComponent(message)}`;
12
- return (_jsxs(Link, { href: whatsappUrl, target: "_blank", rel: "noopener noreferrer", "aria-label": "Get help on WhatsApp", className: "\r\n fixed bottom-6 right-6 z-[9998]\r\n flex items-center gap-0\r\n rounded-full\r\n bg-white\r\n shadow-[0_2px_12px_rgba(0,0,0,0.15)]\r\n hover:shadow-[0_4px_16px_rgba(0,0,0,0.2)]\r\n border border-gray-200\r\n transition-all duration-200\r\n hover:scale-[1.03]\r\n overflow-hidden\r\n no-underline\r\n group\r\n ", children: [_jsx("span", { className: "\r\n pl-4 pr-3 py-2.5\r\n text-sm font-medium text-gray-700\r\n whitespace-nowrap\r\n group-hover:text-gray-900\r\n transition-colors\r\n ", children: "Need help? Chat with us" }), _jsx("span", { className: "flex-shrink-0 pr-0.5 py-0.5", children: _jsx(WhatsappIcon, { size: 42, round: true }) })] }));
13
- }