@joewinke/jatui 0.2.12 → 0.2.13
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/actions/attention.js +21 -13
- package/dist/actions/collapse.js +15 -29
- package/dist/actions/floating.js +29 -37
- package/dist/actions/listNav.js +108 -137
- package/dist/actions/railNav.js +72 -89
- package/dist/actions/staggerEntrance.js +19 -23
- package/dist/actions/tooltip.js +63 -51
- package/dist/assets/sounds/error.js +1 -1
- package/dist/assets/sounds/notify.js +1 -1
- package/dist/assets/sounds/pop.js +1 -1
- package/dist/assets/sounds/send.js +1 -1
- package/dist/assets/sounds/success.js +1 -1
- package/dist/assets/sounds/toggle.js +1 -1
- package/dist/components/graph/theme.js +26 -36
- package/dist/components/markup.js +59 -62
- package/dist/components/session-nav/transcriptModel.js +49 -66
- package/dist/components/shell/shell.js +14 -15
- package/dist/components/table/flexRender.js +2 -2
- package/dist/data/emojis.js +4 -4
- package/dist/dnd/autoScroll.js +10 -12
- package/dist/dnd/createDndBoard.js +94 -185
- package/dist/dnd/draggable.js +15 -23
- package/dist/dnd/dropTarget.js +15 -29
- package/dist/dnd/types.d.ts +9 -0
- package/dist/dnd/types.js +2 -14
- package/dist/showcase/generative/intent.js +19 -21
- package/dist/showcase/generative/manifest.js +27 -80
- package/dist/showcase/scroll/scrollProgress.js +20 -22
- package/dist/showcase/scroll/scrollReveal.js +17 -20
- package/dist/stores/commandPalette.js +18 -46
- package/dist/stores/confirmDialog.js +9 -9
- package/dist/stores/inputDialog.js +9 -9
- package/dist/stores/toast.js +10 -33
- package/dist/transitions/glide.js +29 -25
- package/dist/types/booking.js +4 -4
- package/dist/types/comments.js +2 -2
- package/dist/types/messaging.js +3 -3
- package/dist/types/pipeline.js +2 -2
- package/dist/utils/currency.js +2 -4
- package/dist/utils/daisyuiColors.js +25 -27
- package/dist/utils/dateFormatters.js +44 -44
- package/dist/utils/googleMapsLoader.js +46 -100
- package/dist/utils/mentionParser.js +30 -30
- package/dist/utils/phone.js +21 -27
- package/dist/utils/phoneFormat.js +9 -9
- package/dist/utils/sound.js +27 -78
- package/dist/utils/splitText.js +15 -26
- package/dist/utils/taskUtils.js +1 -1
- package/dist/utils/themeWipe.js +27 -72
- package/dist/utils/viewTransition.js +6 -51
- package/package.json +7 -3
- package/dist/dnd/createDndBoard.test.d.ts +0 -1
- package/dist/dnd/createDndBoard.test.js +0 -261
package/dist/types/comments.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// Threading is ONE level deep on the data side: a comment is either
|
|
7
7
|
// top-level (`parent_id === null`) or a single-level reply. The UI renders
|
|
8
8
|
// this as two visual levels (a comment and its replies).
|
|
9
|
-
export
|
|
9
|
+
export const COMMENT_ENTITY_TYPES = [
|
|
10
10
|
'project',
|
|
11
11
|
'invoice',
|
|
12
12
|
'contract',
|
|
@@ -16,7 +16,7 @@ export var COMMENT_ENTITY_TYPES = [
|
|
|
16
16
|
'milestone',
|
|
17
17
|
'contract_term'
|
|
18
18
|
];
|
|
19
|
-
export
|
|
19
|
+
export const COMMENT_VISIBILITIES = [
|
|
20
20
|
'internal',
|
|
21
21
|
'client'
|
|
22
22
|
];
|
package/dist/types/messaging.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Messaging System Types
|
|
2
2
|
// Generic types for messaging UI components — no framework or backend dependencies
|
|
3
3
|
// ─── Emoji Data Constants ─────────────────────────────────────────────
|
|
4
|
-
export
|
|
4
|
+
export const EMOJI_CATEGORIES = [
|
|
5
5
|
{ id: 'search', name: 'Search', icon: '🔍', description: 'Search results' },
|
|
6
6
|
{
|
|
7
7
|
id: 'frequently_used',
|
|
@@ -59,7 +59,7 @@ export var EMOJI_CATEGORIES = [
|
|
|
59
59
|
description: 'Organization custom emojis'
|
|
60
60
|
}
|
|
61
61
|
];
|
|
62
|
-
export
|
|
62
|
+
export const SKIN_TONES = [
|
|
63
63
|
{ id: 'default', name: 'Default', unicode: '', preview: '👋' },
|
|
64
64
|
{ id: 'light', name: 'Light', unicode: '🏻', preview: '👋🏻' },
|
|
65
65
|
{ id: 'medium-light', name: 'Medium Light', unicode: '🏼', preview: '👋🏼' },
|
|
@@ -68,7 +68,7 @@ export var SKIN_TONES = [
|
|
|
68
68
|
{ id: 'dark', name: 'Dark', unicode: '🏿', preview: '👋🏿' }
|
|
69
69
|
];
|
|
70
70
|
// Default quick-reaction emojis (used when no user preferences available)
|
|
71
|
-
export
|
|
71
|
+
export const DEFAULT_QUICK_REACTIONS = [
|
|
72
72
|
{
|
|
73
73
|
unicode: '👍',
|
|
74
74
|
name: 'Thumbs Up',
|
package/dist/types/pipeline.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// ─── Core Stage/Column Types ───
|
|
2
2
|
// ─── Defaults ───
|
|
3
|
-
export
|
|
3
|
+
export const DEFAULT_DRAG_CONFIG = {
|
|
4
4
|
enabled: true,
|
|
5
5
|
flipDurationMs: 200,
|
|
6
6
|
dropTargetClass: 'outline-dashed outline-2 outline-base-content/20'
|
|
7
7
|
};
|
|
8
|
-
export
|
|
8
|
+
export const DEFAULT_PIPELINE_CONFIG = {
|
|
9
9
|
views: ['kanban'],
|
|
10
10
|
defaultView: 'kanban',
|
|
11
11
|
drag: DEFAULT_DRAG_CONFIG,
|
package/dist/utils/currency.js
CHANGED
|
@@ -5,12 +5,10 @@
|
|
|
5
5
|
* Format a number as USD currency string.
|
|
6
6
|
* Override locale/currency by passing options.
|
|
7
7
|
*/
|
|
8
|
-
export function formatCurrency(amount, locale, currency) {
|
|
9
|
-
if (locale === void 0) { locale = 'en-US'; }
|
|
10
|
-
if (currency === void 0) { currency = 'USD'; }
|
|
8
|
+
export function formatCurrency(amount, locale = 'en-US', currency = 'USD') {
|
|
11
9
|
return new Intl.NumberFormat(locale, {
|
|
12
10
|
style: 'currency',
|
|
13
|
-
currency
|
|
11
|
+
currency,
|
|
14
12
|
minimumFractionDigits: 2,
|
|
15
13
|
maximumFractionDigits: 2
|
|
16
14
|
}).format(amount);
|
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
* Reads --color-* custom properties at runtime so any custom DaisyUI theme
|
|
5
5
|
* works correctly. SSR-safe: falls back to transparent/black when window is absent.
|
|
6
6
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
let colorCache = new Map();
|
|
8
|
+
let themeObserver = null;
|
|
9
9
|
function resolveViaDOM(colorName) {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const el = document.documentElement;
|
|
11
|
+
const raw = getComputedStyle(el).getPropertyValue(`--color-${colorName}`).trim();
|
|
12
12
|
if (raw)
|
|
13
13
|
return raw;
|
|
14
14
|
// DaisyUI v4 used shortened oklch channel vars (e.g. --p); try that form
|
|
15
|
-
|
|
15
|
+
const shortMap = {
|
|
16
16
|
primary: '--p',
|
|
17
17
|
'primary-content': '--pc',
|
|
18
18
|
secondary: '--s',
|
|
@@ -34,11 +34,11 @@ function resolveViaDOM(colorName) {
|
|
|
34
34
|
error: '--er',
|
|
35
35
|
'error-content': '--erc',
|
|
36
36
|
};
|
|
37
|
-
|
|
37
|
+
const shortVar = shortMap[colorName];
|
|
38
38
|
if (shortVar) {
|
|
39
|
-
|
|
39
|
+
const shortRaw = getComputedStyle(el).getPropertyValue(shortVar).trim();
|
|
40
40
|
if (shortRaw)
|
|
41
|
-
return
|
|
41
|
+
return `oklch(${shortRaw})`;
|
|
42
42
|
}
|
|
43
43
|
return 'oklch(0.5 0 0)';
|
|
44
44
|
}
|
|
@@ -52,25 +52,23 @@ export function getDaisyUIColor(colorName) {
|
|
|
52
52
|
if (colorCache.has(colorName)) {
|
|
53
53
|
return colorCache.get(colorName);
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
const resolved = resolveViaDOM(colorName);
|
|
56
56
|
colorCache.set(colorName, resolved);
|
|
57
57
|
return resolved;
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
60
|
* Create a lighter version of a color using color-mix (works with hex and oklch).
|
|
61
61
|
*/
|
|
62
|
-
export function lightenColor(color, amount) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return "color-mix(in oklch, white ".concat(pct, "%, ").concat(color, ")");
|
|
62
|
+
export function lightenColor(color, amount = 0.2) {
|
|
63
|
+
const pct = Math.round(amount * 100);
|
|
64
|
+
return `color-mix(in oklch, white ${pct}%, ${color})`;
|
|
66
65
|
}
|
|
67
66
|
/**
|
|
68
67
|
* Create a darker version of a color using color-mix.
|
|
69
68
|
*/
|
|
70
|
-
export function darkenColor(color, amount) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return "color-mix(in oklch, black ".concat(pct, "%, ").concat(color, ")");
|
|
69
|
+
export function darkenColor(color, amount = 0.2) {
|
|
70
|
+
const pct = Math.round(amount * 100);
|
|
71
|
+
return `color-mix(in oklch, black ${pct}%, ${color})`;
|
|
74
72
|
}
|
|
75
73
|
/**
|
|
76
74
|
* Setup theme change detection.
|
|
@@ -80,9 +78,9 @@ export function watchThemeChanges(onThemeChange) {
|
|
|
80
78
|
if (themeObserver) {
|
|
81
79
|
themeObserver.disconnect();
|
|
82
80
|
}
|
|
83
|
-
themeObserver = new MutationObserver(
|
|
84
|
-
|
|
85
|
-
mutations.forEach(
|
|
81
|
+
themeObserver = new MutationObserver((mutations) => {
|
|
82
|
+
let themeChanged = false;
|
|
83
|
+
mutations.forEach((mutation) => {
|
|
86
84
|
if (mutation.type === 'attributes' &&
|
|
87
85
|
(mutation.attributeName === 'data-theme' ||
|
|
88
86
|
mutation.attributeName === 'class')) {
|
|
@@ -100,7 +98,7 @@ export function watchThemeChanges(onThemeChange) {
|
|
|
100
98
|
attributes: true,
|
|
101
99
|
attributeFilter: ['data-theme', 'class'],
|
|
102
100
|
});
|
|
103
|
-
return
|
|
101
|
+
return () => {
|
|
104
102
|
if (themeObserver) {
|
|
105
103
|
themeObserver.disconnect();
|
|
106
104
|
themeObserver = null;
|
|
@@ -114,25 +112,25 @@ export function watchThemeChanges(onThemeChange) {
|
|
|
114
112
|
export function isDarkTheme() {
|
|
115
113
|
if (typeof window === 'undefined')
|
|
116
114
|
return false;
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
const baseContent = getDaisyUIColor('base-content');
|
|
116
|
+
const base100 = getDaisyUIColor('base-100');
|
|
119
117
|
return getLuminance(baseContent) > getLuminance(base100);
|
|
120
118
|
}
|
|
121
119
|
function getLuminance(color) {
|
|
122
120
|
// Resolve via a temporary off-screen canvas pixel
|
|
123
|
-
|
|
121
|
+
const canvas = document.createElement('canvas');
|
|
124
122
|
canvas.width = 1;
|
|
125
123
|
canvas.height = 1;
|
|
126
|
-
|
|
124
|
+
const ctx = canvas.getContext('2d');
|
|
127
125
|
if (!ctx)
|
|
128
126
|
return 0.5;
|
|
129
127
|
try {
|
|
130
128
|
ctx.fillStyle = color;
|
|
131
129
|
ctx.fillRect(0, 0, 1, 1);
|
|
132
|
-
|
|
130
|
+
const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data;
|
|
133
131
|
return (r * 299 + g * 587 + b * 114) / (1000 * 255);
|
|
134
132
|
}
|
|
135
|
-
catch
|
|
133
|
+
catch {
|
|
136
134
|
return 0.5;
|
|
137
135
|
}
|
|
138
136
|
}
|
|
@@ -30,8 +30,8 @@ export function parseTimestamp(timestamp) {
|
|
|
30
30
|
return null;
|
|
31
31
|
if (timestamp instanceof Date)
|
|
32
32
|
return isNaN(timestamp.getTime()) ? null : timestamp;
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const normalized = normalizeTimestamp(timestamp);
|
|
34
|
+
const date = new Date(normalized);
|
|
35
35
|
return isNaN(date.getTime()) ? null : date;
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
@@ -41,30 +41,30 @@ export function parseTimestamp(timestamp) {
|
|
|
41
41
|
export function formatRelativeTime(dateStr) {
|
|
42
42
|
if (!dateStr)
|
|
43
43
|
return '-';
|
|
44
|
-
|
|
44
|
+
const date = parseTimestamp(dateStr);
|
|
45
45
|
if (!date)
|
|
46
46
|
return '-';
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
const now = new Date();
|
|
48
|
+
const diffMs = now.getTime() - date.getTime();
|
|
49
|
+
const diffMins = Math.floor(diffMs / (1000 * 60));
|
|
50
|
+
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
51
|
+
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
52
|
+
const diffWeeks = Math.floor(diffDays / 7);
|
|
53
|
+
const diffMonths = Math.floor(diffDays / 30);
|
|
54
|
+
const diffYears = Math.floor(diffDays / 365);
|
|
55
55
|
if (diffMins < 1)
|
|
56
56
|
return 'now';
|
|
57
57
|
if (diffMins < 60)
|
|
58
|
-
return
|
|
58
|
+
return `${diffMins}m`;
|
|
59
59
|
if (diffHours < 24)
|
|
60
|
-
return
|
|
60
|
+
return `${diffHours}h`;
|
|
61
61
|
if (diffDays < 7)
|
|
62
|
-
return
|
|
62
|
+
return `${diffDays}d`;
|
|
63
63
|
if (diffWeeks < 4)
|
|
64
|
-
return
|
|
64
|
+
return `${diffWeeks}w`;
|
|
65
65
|
if (diffMonths < 12)
|
|
66
|
-
return
|
|
67
|
-
return
|
|
66
|
+
return `${diffMonths}mo`;
|
|
67
|
+
return `${diffYears}y`;
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
70
|
* Format full date and time for tooltips and detailed views.
|
|
@@ -72,7 +72,7 @@ export function formatRelativeTime(dateStr) {
|
|
|
72
72
|
export function formatFullDate(dateStr) {
|
|
73
73
|
if (!dateStr)
|
|
74
74
|
return '';
|
|
75
|
-
|
|
75
|
+
const date = parseTimestamp(dateStr);
|
|
76
76
|
if (!date)
|
|
77
77
|
return '';
|
|
78
78
|
return date.toLocaleString('en-US', {
|
|
@@ -89,7 +89,7 @@ export function formatFullDate(dateStr) {
|
|
|
89
89
|
export function formatShortDate(dateStr) {
|
|
90
90
|
if (!dateStr)
|
|
91
91
|
return '';
|
|
92
|
-
|
|
92
|
+
const date = parseTimestamp(dateStr);
|
|
93
93
|
if (!date)
|
|
94
94
|
return '';
|
|
95
95
|
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
@@ -101,21 +101,21 @@ export function formatShortDate(dateStr) {
|
|
|
101
101
|
export function formatLastActivity(timestamp) {
|
|
102
102
|
if (!timestamp)
|
|
103
103
|
return 'Never';
|
|
104
|
-
|
|
104
|
+
const date = parseTimestamp(timestamp);
|
|
105
105
|
if (!date)
|
|
106
106
|
return 'Never';
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
const now = new Date();
|
|
108
|
+
const diffMs = now.getTime() - date.getTime();
|
|
109
|
+
const diffMins = Math.floor(diffMs / 60000);
|
|
110
|
+
const diffHours = Math.floor(diffMs / 3600000);
|
|
111
|
+
const diffDays = Math.floor(diffMs / 86400000);
|
|
112
112
|
if (diffMins < 1)
|
|
113
113
|
return 'Just now';
|
|
114
114
|
if (diffMins < 60)
|
|
115
|
-
return
|
|
115
|
+
return `${diffMins}m ago`;
|
|
116
116
|
if (diffHours < 24)
|
|
117
|
-
return
|
|
118
|
-
return
|
|
117
|
+
return `${diffHours}h ago`;
|
|
118
|
+
return `${diffDays}d ago`;
|
|
119
119
|
}
|
|
120
120
|
/**
|
|
121
121
|
* Format date for display (simple locale string).
|
|
@@ -123,7 +123,7 @@ export function formatLastActivity(timestamp) {
|
|
|
123
123
|
export function formatDate(dateString) {
|
|
124
124
|
if (!dateString)
|
|
125
125
|
return 'N/A';
|
|
126
|
-
|
|
126
|
+
const date = parseTimestamp(dateString);
|
|
127
127
|
if (!date)
|
|
128
128
|
return 'N/A';
|
|
129
129
|
return date.toLocaleString();
|
|
@@ -134,7 +134,7 @@ export function formatDate(dateString) {
|
|
|
134
134
|
export function getTimeSinceMs(timestamp) {
|
|
135
135
|
if (!timestamp)
|
|
136
136
|
return Infinity;
|
|
137
|
-
|
|
137
|
+
const date = parseTimestamp(timestamp);
|
|
138
138
|
if (!date)
|
|
139
139
|
return Infinity;
|
|
140
140
|
return Date.now() - date.getTime();
|
|
@@ -158,7 +158,7 @@ export function isWithinMinutes(timestamp, minutes) {
|
|
|
158
158
|
export function formatDateTime(dateStr) {
|
|
159
159
|
if (!dateStr)
|
|
160
160
|
return '';
|
|
161
|
-
|
|
161
|
+
const date = parseTimestamp(dateStr);
|
|
162
162
|
if (!date)
|
|
163
163
|
return '';
|
|
164
164
|
return date.toLocaleString(undefined, {
|
|
@@ -178,24 +178,24 @@ export function formatDateTime(dateStr) {
|
|
|
178
178
|
* // <span style="color: {age.color}">{age.label}</span>
|
|
179
179
|
*/
|
|
180
180
|
export function taskAge(dateStr) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
181
|
+
const now = Date.now();
|
|
182
|
+
const created = new Date(dateStr).getTime();
|
|
183
|
+
const minutes = Math.floor((now - created) / 60000);
|
|
184
|
+
const hours = Math.floor(minutes / 60);
|
|
185
|
+
const days = Math.floor(hours / 24);
|
|
186
|
+
const weeks = Math.floor(days / 7);
|
|
187
|
+
const months = Math.floor(days / 30);
|
|
188
188
|
if (minutes < 60)
|
|
189
|
-
return { label:
|
|
189
|
+
return { label: `${minutes}m`, color: 'oklch(0.75 0.18 145)' }; // green — fresh
|
|
190
190
|
if (hours < 24)
|
|
191
|
-
return { label:
|
|
191
|
+
return { label: `${hours}h`, color: 'oklch(0.75 0.18 145)' }; // green
|
|
192
192
|
if (days < 3)
|
|
193
|
-
return { label:
|
|
193
|
+
return { label: `${days}d`, color: 'oklch(0.70 0.15 200)' }; // blue — recent
|
|
194
194
|
if (days < 7)
|
|
195
|
-
return { label:
|
|
195
|
+
return { label: `${days}d`, color: 'oklch(0.75 0.15 85)' }; // amber — aging
|
|
196
196
|
if (weeks < 4)
|
|
197
|
-
return { label:
|
|
198
|
-
return { label:
|
|
197
|
+
return { label: `${weeks}w`, color: 'oklch(0.65 0.18 50)' }; // orange — stale
|
|
198
|
+
return { label: `${months}mo`, color: 'oklch(0.65 0.20 25)' }; // red — old
|
|
199
199
|
}
|
|
200
200
|
/**
|
|
201
201
|
* Generic 3-state pacing color: green while fresh, amber once aging past
|
|
@@ -8,114 +8,60 @@
|
|
|
8
8
|
* await loadGoogleMapsAPI({ apiKey: 'YOUR_KEY', libraries: ['marker', 'geometry'] })
|
|
9
9
|
* if (isGoogleMapsLoaded()) { ... }
|
|
10
10
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
21
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
22
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
23
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
24
|
-
function step(op) {
|
|
25
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
26
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
27
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
28
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
29
|
-
switch (op[0]) {
|
|
30
|
-
case 0: case 1: t = op; break;
|
|
31
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
32
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
33
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
34
|
-
default:
|
|
35
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
36
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
37
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
38
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
39
|
-
if (t[2]) _.ops.pop();
|
|
40
|
-
_.trys.pop(); continue;
|
|
41
|
-
}
|
|
42
|
-
op = body.call(thisArg, _);
|
|
43
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
44
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
48
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
49
|
-
if (ar || !(i in from)) {
|
|
50
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
51
|
-
ar[i] = from[i];
|
|
52
|
-
}
|
|
11
|
+
let loadPromise = null;
|
|
12
|
+
let isLoaded = false;
|
|
13
|
+
export async function loadGoogleMapsAPI(options) {
|
|
14
|
+
if (typeof window === 'undefined')
|
|
15
|
+
return;
|
|
16
|
+
const { apiKey, libraries = [] } = options;
|
|
17
|
+
if (!apiKey) {
|
|
18
|
+
console.warn('googleMapsLoader: No API key provided');
|
|
19
|
+
return;
|
|
53
20
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return [2 /*return*/];
|
|
65
|
-
apiKey = options.apiKey, _a = options.libraries, libraries = _a === void 0 ? [] : _a;
|
|
66
|
-
if (!apiKey) {
|
|
67
|
-
console.warn('googleMapsLoader: No API key provided');
|
|
68
|
-
return [2 /*return*/];
|
|
69
|
-
}
|
|
70
|
-
// If already loaded and all required libraries are available, return
|
|
71
|
-
if (isLoaded && ((_b = window.google) === null || _b === void 0 ? void 0 : _b.maps)) {
|
|
72
|
-
allLibrariesAvailable = libraries.every(function (lib) {
|
|
73
|
-
var _a, _b, _c, _d;
|
|
74
|
-
switch (lib) {
|
|
75
|
-
case 'places':
|
|
76
|
-
return (_b = (_a = window.google) === null || _a === void 0 ? void 0 : _a.maps) === null || _b === void 0 ? void 0 : _b.places;
|
|
77
|
-
case 'geometry':
|
|
78
|
-
return (_d = (_c = window.google) === null || _c === void 0 ? void 0 : _c.maps) === null || _d === void 0 ? void 0 : _d.geometry;
|
|
79
|
-
default:
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
if (allLibrariesAvailable)
|
|
84
|
-
return [2 /*return*/];
|
|
21
|
+
// If already loaded and all required libraries are available, return
|
|
22
|
+
if (isLoaded && window.google?.maps) {
|
|
23
|
+
const allLibrariesAvailable = libraries.every((lib) => {
|
|
24
|
+
switch (lib) {
|
|
25
|
+
case 'places':
|
|
26
|
+
return window.google?.maps?.places;
|
|
27
|
+
case 'geometry':
|
|
28
|
+
return window.google?.maps?.geometry;
|
|
29
|
+
default:
|
|
30
|
+
return true;
|
|
85
31
|
}
|
|
86
|
-
// If already loading, return the existing promise
|
|
87
|
-
if (loadPromise)
|
|
88
|
-
return [2 /*return*/, loadPromise];
|
|
89
|
-
allLibraries = Array.from(new Set(__spreadArray(['geometry', 'places', 'marker'], libraries, true)));
|
|
90
|
-
loadPromise = new Promise(function (resolve, reject) {
|
|
91
|
-
var script = document.createElement('script');
|
|
92
|
-
script.src = "https://maps.googleapis.com/maps/api/js?key=".concat(apiKey, "&libraries=").concat(allLibraries.join(','));
|
|
93
|
-
script.async = true;
|
|
94
|
-
script.defer = true;
|
|
95
|
-
script.onload = function () {
|
|
96
|
-
isLoaded = true;
|
|
97
|
-
loadPromise = null;
|
|
98
|
-
resolve();
|
|
99
|
-
};
|
|
100
|
-
script.onerror = function (error) {
|
|
101
|
-
loadPromise = null;
|
|
102
|
-
reject(error);
|
|
103
|
-
};
|
|
104
|
-
document.head.appendChild(script);
|
|
105
|
-
});
|
|
106
|
-
return [2 /*return*/, loadPromise];
|
|
107
32
|
});
|
|
33
|
+
if (allLibrariesAvailable)
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// If already loading, return the existing promise
|
|
37
|
+
if (loadPromise)
|
|
38
|
+
return loadPromise;
|
|
39
|
+
// Combine default libraries with requested ones
|
|
40
|
+
const allLibraries = Array.from(new Set(['geometry', 'places', 'marker', ...libraries]));
|
|
41
|
+
loadPromise = new Promise((resolve, reject) => {
|
|
42
|
+
const script = document.createElement('script');
|
|
43
|
+
script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=${allLibraries.join(',')}`;
|
|
44
|
+
script.async = true;
|
|
45
|
+
script.defer = true;
|
|
46
|
+
script.onload = () => {
|
|
47
|
+
isLoaded = true;
|
|
48
|
+
loadPromise = null;
|
|
49
|
+
resolve();
|
|
50
|
+
};
|
|
51
|
+
script.onerror = (error) => {
|
|
52
|
+
loadPromise = null;
|
|
53
|
+
reject(error);
|
|
54
|
+
};
|
|
55
|
+
document.head.appendChild(script);
|
|
108
56
|
});
|
|
57
|
+
return loadPromise;
|
|
109
58
|
}
|
|
110
59
|
export function isGoogleMapsLoaded() {
|
|
111
|
-
|
|
112
|
-
return isLoaded && !!((_a = window.google) === null || _a === void 0 ? void 0 : _a.maps);
|
|
60
|
+
return isLoaded && !!window.google?.maps;
|
|
113
61
|
}
|
|
114
62
|
export function isPlacesLoaded() {
|
|
115
|
-
|
|
116
|
-
return isGoogleMapsLoaded() && !!((_b = (_a = window.google) === null || _a === void 0 ? void 0 : _a.maps) === null || _b === void 0 ? void 0 : _b.places);
|
|
63
|
+
return isGoogleMapsLoaded() && !!window.google?.maps?.places;
|
|
117
64
|
}
|
|
118
65
|
export function isGeometryLoaded() {
|
|
119
|
-
|
|
120
|
-
return isGoogleMapsLoaded() && !!((_b = (_a = window.google) === null || _a === void 0 ? void 0 : _a.maps) === null || _b === void 0 ? void 0 : _b.geometry);
|
|
66
|
+
return isGoogleMapsLoaded() && !!window.google?.maps?.geometry;
|
|
121
67
|
}
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* routing via the `urlGenerator` parameter or by setting a default.
|
|
9
9
|
*/
|
|
10
10
|
// Regex to match mention pattern: [@Display Name](type:id)
|
|
11
|
-
|
|
11
|
+
const MENTION_REGEX = /\[@([^\]]+)\]\(([^:]+):([^)]+)\)/g;
|
|
12
12
|
// Default URL generator (apps can override)
|
|
13
|
-
|
|
13
|
+
let defaultUrlGenerator = (type, id) => `#${type}/${id}`;
|
|
14
14
|
/**
|
|
15
15
|
* Set the default URL generator for mentions.
|
|
16
16
|
* Call this once at app startup to configure mention link routing.
|
|
@@ -22,20 +22,20 @@ export function setMentionUrlGenerator(generator) {
|
|
|
22
22
|
* Parse message content and extract all mentions
|
|
23
23
|
*/
|
|
24
24
|
export function parseMentions(content, urlGenerator) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
const mentions = [];
|
|
26
|
+
const gen = urlGenerator ?? defaultUrlGenerator;
|
|
27
|
+
let match;
|
|
28
28
|
MENTION_REGEX.lastIndex = 0;
|
|
29
29
|
while ((match = MENTION_REGEX.exec(content)) !== null) {
|
|
30
|
-
|
|
30
|
+
const [fullMatch, displayName, type, id] = match;
|
|
31
31
|
mentions.push({
|
|
32
|
-
type
|
|
33
|
-
id
|
|
34
|
-
displayName
|
|
32
|
+
type,
|
|
33
|
+
id,
|
|
34
|
+
displayName,
|
|
35
35
|
url: gen(type, id),
|
|
36
36
|
startIndex: match.index,
|
|
37
37
|
endIndex: match.index + fullMatch.length,
|
|
38
|
-
fullMatch
|
|
38
|
+
fullMatch
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
return mentions;
|
|
@@ -44,15 +44,15 @@ export function parseMentions(content, urlGenerator) {
|
|
|
44
44
|
* Convert message content with mentions to HTML
|
|
45
45
|
*/
|
|
46
46
|
export function renderMentionsAsHTML(content, urlGenerator) {
|
|
47
|
-
|
|
47
|
+
const mentions = parseMentions(content, urlGenerator);
|
|
48
48
|
if (mentions.length === 0) {
|
|
49
49
|
return escapeHTML(content);
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
mentions.forEach(
|
|
51
|
+
let result = '';
|
|
52
|
+
let lastIndex = 0;
|
|
53
|
+
mentions.forEach((mention) => {
|
|
54
54
|
result += escapeHTML(content.substring(lastIndex, mention.startIndex));
|
|
55
|
-
result +=
|
|
55
|
+
result += `<a href="${mention.url}" class="mention-link mention-${mention.type}" data-mention-type="${mention.type}" data-mention-id="${mention.id}">@${escapeHTML(mention.displayName)}</a>`;
|
|
56
56
|
lastIndex = mention.endIndex;
|
|
57
57
|
});
|
|
58
58
|
result += escapeHTML(content.substring(lastIndex));
|
|
@@ -62,7 +62,7 @@ export function renderMentionsAsHTML(content, urlGenerator) {
|
|
|
62
62
|
* Create a mention string for insertion into message input
|
|
63
63
|
*/
|
|
64
64
|
export function createMentionString(type, id, displayName) {
|
|
65
|
-
return
|
|
65
|
+
return `[@${displayName}](${type}:${id})`;
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
68
|
* Extract plain text from message content (remove mention markup)
|
|
@@ -75,18 +75,18 @@ export function extractPlainText(content) {
|
|
|
75
75
|
* Check if cursor is inside a mention
|
|
76
76
|
*/
|
|
77
77
|
export function findMentionAtCursor(content, cursorPosition) {
|
|
78
|
-
|
|
79
|
-
return (mentions.find(
|
|
78
|
+
const mentions = parseMentions(content);
|
|
79
|
+
return (mentions.find((mention) => cursorPosition >= mention.startIndex && cursorPosition <= mention.endIndex) || null);
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
82
|
* Find @ symbol and following text that could be a mention being typed.
|
|
83
83
|
* Supported symbols: @ $ # ! ~ / +
|
|
84
84
|
*/
|
|
85
85
|
export function findPartialMention(content, cursorPosition) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
for (
|
|
89
|
-
|
|
86
|
+
const mentionSymbols = ['@', '$', '#', '!', '~', '/', '+'];
|
|
87
|
+
let symbolIndex = -1;
|
|
88
|
+
for (let i = cursorPosition - 1; i >= 0; i--) {
|
|
89
|
+
const char = content[i];
|
|
90
90
|
if (mentionSymbols.includes(char)) {
|
|
91
91
|
symbolIndex = i;
|
|
92
92
|
break;
|
|
@@ -97,21 +97,21 @@ export function findPartialMention(content, cursorPosition) {
|
|
|
97
97
|
}
|
|
98
98
|
if (symbolIndex === -1)
|
|
99
99
|
return null;
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
const query = content.substring(symbolIndex + 1, cursorPosition).trim();
|
|
101
|
+
const existingMention = findMentionAtCursor(content, symbolIndex);
|
|
102
102
|
if (existingMention)
|
|
103
103
|
return null;
|
|
104
|
-
return { start: symbolIndex, query
|
|
104
|
+
return { start: symbolIndex, query };
|
|
105
105
|
}
|
|
106
106
|
/**
|
|
107
107
|
* Replace partial mention with complete mention string
|
|
108
108
|
*/
|
|
109
109
|
export function replaceMentionInContent(content, startIndex, endIndex, mentionString) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return { newContent
|
|
110
|
+
const before = content.substring(0, startIndex);
|
|
111
|
+
const after = content.substring(endIndex);
|
|
112
|
+
const newContent = before + mentionString + after;
|
|
113
|
+
const newCursorPosition = startIndex + mentionString.length;
|
|
114
|
+
return { newContent, newCursorPosition };
|
|
115
115
|
}
|
|
116
116
|
/**
|
|
117
117
|
* Simple HTML escape function
|