droplinked-editor-configs 1.9.10 → 1.9.11
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/dist/droplinked-editor.es.js +5279 -5279
- package/dist/droplinked-editor.umd.js +43 -43
- package/dist/lib/stores/app/appStore.d.ts +3 -12
- package/dist/lib/utils/urlUtils.d.ts +11 -0
- package/package.json +1 -1
- package/src/components/SocialMediaBar/SocialMediaBar.tsx +18 -35
- package/src/components/header/Sidebar.tsx +1 -1
- package/src/lib/stores/app/appStore.ts +1 -1
- package/src/lib/utils/urlUtils.ts +40 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -12,10 +12,7 @@ export interface IAppStore {
|
|
|
12
12
|
states: {
|
|
13
13
|
cart: Icart;
|
|
14
14
|
user: {
|
|
15
|
-
[shopName: string]:
|
|
16
|
-
token: string;
|
|
17
|
-
user: IUser;
|
|
18
|
-
};
|
|
15
|
+
[shopName: string]: IUser;
|
|
19
16
|
} | null;
|
|
20
17
|
shop: IShop;
|
|
21
18
|
};
|
|
@@ -34,10 +31,7 @@ declare const useAppStore: import('zustand').UseBoundStore<Omit<Omit<import('zus
|
|
|
34
31
|
states: {
|
|
35
32
|
cart: Icart;
|
|
36
33
|
user: {
|
|
37
|
-
[shopName: string]:
|
|
38
|
-
token: string;
|
|
39
|
-
user: IUser;
|
|
40
|
-
};
|
|
34
|
+
[shopName: string]: IUser;
|
|
41
35
|
} | null;
|
|
42
36
|
shop: IShop;
|
|
43
37
|
};
|
|
@@ -51,10 +45,7 @@ declare const useAppStore: import('zustand').UseBoundStore<Omit<Omit<import('zus
|
|
|
51
45
|
states: {
|
|
52
46
|
cart: Icart;
|
|
53
47
|
user: {
|
|
54
|
-
[shopName: string]:
|
|
55
|
-
token: string;
|
|
56
|
-
user: IUser;
|
|
57
|
-
};
|
|
48
|
+
[shopName: string]: IUser;
|
|
58
49
|
} | null;
|
|
59
50
|
shop: IShop;
|
|
60
51
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if a given string is likely an external URL (domain or http/https).
|
|
3
|
+
* It returns true for "google.com", "www.test.com", "http://site.com".
|
|
4
|
+
* It returns false for "home", "/about", "contact-us".
|
|
5
|
+
*/
|
|
6
|
+
export declare const isExternalUrl: (url: string) => boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Normalizes a URL string.
|
|
9
|
+
* If it's detected as an external URL but missing protocol, prepends https://.
|
|
10
|
+
*/
|
|
11
|
+
export declare const normalizeUrl: (url: string) => string;
|
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { socialMediaLinks } from './socialLinks';
|
|
3
3
|
import CustomIcon from 'components/customIcon/CustomIcon';
|
|
4
4
|
import useThemeInfo from 'hooks/useThemeInfo';
|
|
5
|
+
import { isExternalUrl, normalizeUrl } from 'lib/utils/urlUtils';
|
|
5
6
|
|
|
6
7
|
// Accept socialChannels as prop
|
|
7
8
|
interface SocialChannel {
|
|
@@ -25,12 +26,11 @@ interface SocialMediaBarProps {
|
|
|
25
26
|
const SocialMediaBar: React.FC<SocialMediaBarProps> = React.memo(({ socialChannels }) => {
|
|
26
27
|
const { shopDesign: { foreground } } = useThemeInfo()
|
|
27
28
|
|
|
28
|
-
// Map channel name to
|
|
29
|
-
const
|
|
30
|
-
|
|
29
|
+
// Map channel name to social link item
|
|
30
|
+
const getSocialLinkItem = (channel: string) => {
|
|
31
|
+
return socialMediaLinks.find(
|
|
31
32
|
(item) => item.key.replace('URL', '').toLowerCase() === channel.toLowerCase()
|
|
32
33
|
);
|
|
33
|
-
return found ? found.icon : undefined;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
return (
|
|
@@ -39,12 +39,22 @@ const SocialMediaBar: React.FC<SocialMediaBarProps> = React.memo(({ socialChanne
|
|
|
39
39
|
.filter((item) => !!item.links?.url && !!item.links?.channel)
|
|
40
40
|
.map((item, idx) => {
|
|
41
41
|
const { channel, url } = item.links!;
|
|
42
|
-
const
|
|
43
|
-
if (!
|
|
42
|
+
const socialItem = getSocialLinkItem(channel!);
|
|
43
|
+
if (!socialItem) return null;
|
|
44
|
+
|
|
45
|
+
const Icon = socialItem.icon;
|
|
46
|
+
let href = url!;
|
|
47
|
+
|
|
48
|
+
if (isExternalUrl(href)) {
|
|
49
|
+
href = normalizeUrl(href);
|
|
50
|
+
} else {
|
|
51
|
+
href = `${socialItem.link}${href.replace(/^\//, '')}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
return (
|
|
45
55
|
<a
|
|
46
56
|
key={channel + idx}
|
|
47
|
-
href={
|
|
57
|
+
href={href}
|
|
48
58
|
target="_blank"
|
|
49
59
|
rel="noopener noreferrer"
|
|
50
60
|
className="inline-block rounded-md p-[10px] transition-colors duration-100 ease-out"
|
|
@@ -58,31 +68,4 @@ const SocialMediaBar: React.FC<SocialMediaBarProps> = React.memo(({ socialChanne
|
|
|
58
68
|
);
|
|
59
69
|
});
|
|
60
70
|
|
|
61
|
-
export default SocialMediaBar;
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Utility function to darken a hexadecimal color by a percentage
|
|
65
|
-
*
|
|
66
|
-
* @param color - Hexadecimal color string (e.g., "#FFFFFF")
|
|
67
|
-
* @param percent - Percentage to darken the color (-100 to 100)
|
|
68
|
-
* @returns Darkened hexadecimal color string
|
|
69
|
-
*/
|
|
70
|
-
const darkenColor = (color: string, percent: number): string => {
|
|
71
|
-
let num = parseInt(color.slice(1), 16);
|
|
72
|
-
let amt = Math.round(2.55 * percent);
|
|
73
|
-
let R = (num >> 16) + amt;
|
|
74
|
-
let G = ((num >> 8) & 0x00ff) + amt;
|
|
75
|
-
let B = (num & 0x0000ff) + amt;
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
'#' +
|
|
79
|
-
(
|
|
80
|
-
0x1000000 +
|
|
81
|
-
(R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 +
|
|
82
|
-
(G < 255 ? (G < 1 ? 0 : G) : 255) * 0x100 +
|
|
83
|
-
(B < 255 ? (B < 1 ? 0 : B) : 255)
|
|
84
|
-
)
|
|
85
|
-
.toString(16)
|
|
86
|
-
.slice(1)
|
|
87
|
-
);
|
|
88
|
-
};
|
|
71
|
+
export default SocialMediaBar;
|
|
@@ -18,7 +18,7 @@ interface Props {
|
|
|
18
18
|
function Sidebar({ iconColor, linkManagement, ProfileDropdownWrapper }: Props) {
|
|
19
19
|
const { isOpen, onOpen, onClose } = useDisclosure()
|
|
20
20
|
const { states: { user, shop: { url } } } = useAppStore()
|
|
21
|
-
const { id } = user?.[url]
|
|
21
|
+
const { id } = user?.[url] || {}
|
|
22
22
|
const { shopDesign: { backgroundBody } } = useThemeInfo()
|
|
23
23
|
const [isLargerThan768] = useMediaQuery('(min-width: 768px)')
|
|
24
24
|
const { defineMultiStyleConfig } = createMultiStyleConfigHelpers(parts.keys)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if a given string is likely an external URL (domain or http/https).
|
|
3
|
+
* It returns true for "google.com", "www.test.com", "http://site.com".
|
|
4
|
+
* It returns false for "home", "/about", "contact-us".
|
|
5
|
+
*/
|
|
6
|
+
export const isExternalUrl = (url: string): boolean => {
|
|
7
|
+
if (!url) return false;
|
|
8
|
+
|
|
9
|
+
// Already has protocol
|
|
10
|
+
if (/^https?:\/\//i.test(url)) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Check for domain-like pattern (e.g., "example.com", "sub.domain.net/path")
|
|
15
|
+
// Pattern: starts with alphanumeric, contains dots, ends with TLD (2+ chars)
|
|
16
|
+
// We exclude strings that start with '/' (absolute paths)
|
|
17
|
+
if (url.startsWith('/')) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Regex to match domain at the start of the string
|
|
22
|
+
// [domain part].[tld] followed by optional path/query
|
|
23
|
+
const domainRegex = /^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}(\/.*)?$/;
|
|
24
|
+
|
|
25
|
+
return domainRegex.test(url);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Normalizes a URL string.
|
|
30
|
+
* If it's detected as an external URL but missing protocol, prepends https://.
|
|
31
|
+
*/
|
|
32
|
+
export const normalizeUrl = (url: string): string => {
|
|
33
|
+
if (!url) return url;
|
|
34
|
+
|
|
35
|
+
if (isExternalUrl(url) && !/^https?:\/\//i.test(url)) {
|
|
36
|
+
return `https://${url}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return url;
|
|
40
|
+
};
|