beez-ui 0.7.0 → 0.8.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.
- package/CHANGELOG.md +21 -3
- package/README.md +290 -274
- package/dist/components/account-menu.d.ts +53 -0
- package/dist/components/account-menu.js +405 -0
- package/dist/components/animated-collapse.d.ts +19 -0
- package/dist/components/animated-collapse.js +81 -0
- package/dist/components/animated-count.d.ts +13 -0
- package/dist/components/animated-count.js +105 -0
- package/dist/components/animated-list-item.d.ts +25 -0
- package/dist/components/animated-list-item.js +132 -0
- package/dist/components/bouncing-dots-loader.d.ts +19 -0
- package/dist/components/bouncing-dots-loader.js +47 -0
- package/dist/components/confirm-delete-button.d.ts +29 -0
- package/dist/components/confirm-delete-button.js +254 -0
- package/dist/components/empty-state.d.ts +24 -0
- package/dist/components/empty-state.js +37 -0
- package/dist/components/error-state.d.ts +20 -0
- package/dist/components/error-state.js +145 -0
- package/dist/components/external-browser-handoff.d.ts +27 -0
- package/dist/components/external-browser-handoff.js +306 -0
- package/dist/components/file-upload.d.ts +78 -0
- package/dist/components/file-upload.js +678 -0
- package/dist/components/info-popover.d.ts +23 -0
- package/dist/components/info-popover.js +158 -0
- package/dist/components/month-calendar-header.d.ts +65 -0
- package/dist/components/month-calendar-header.js +337 -0
- package/dist/components/month-grid.d.ts +59 -0
- package/dist/components/month-grid.js +301 -0
- package/dist/components/notification-bell.d.ts +50 -0
- package/dist/components/notification-bell.js +338 -0
- package/dist/components/notification-panel.d.ts +56 -0
- package/dist/components/notification-panel.js +281 -0
- package/dist/components/open-in-browser-cta.d.ts +22 -0
- package/dist/components/open-in-browser-cta.js +61 -0
- package/dist/components/presence-swap.d.ts +22 -0
- package/dist/components/presence-swap.js +162 -0
- package/dist/components/progress-ring.d.ts +20 -0
- package/dist/components/progress-ring.js +53 -0
- package/dist/components/pwa-update-control.d.ts +18 -0
- package/dist/components/pwa-update-control.js +119 -0
- package/dist/components/reaction-button.d.ts +26 -0
- package/dist/components/reaction-button.js +114 -0
- package/dist/components/rich-link-editor.d.ts +25 -0
- package/dist/components/rich-link-editor.js +320 -0
- package/dist/components/rich-markdown-content.d.ts +10 -0
- package/dist/components/rich-markdown-content.js +56 -0
- package/dist/components/rich-text-content.d.ts +19 -0
- package/dist/components/rich-text-content.js +26 -0
- package/dist/emoji-picker.d.ts +27 -0
- package/dist/emoji-picker.js +283 -0
- package/dist/hooks/use-horizontal-swipe.d.ts +23 -0
- package/dist/hooks/use-horizontal-swipe.js +80 -0
- package/dist/hooks/use-is-hydrated.d.ts +6 -0
- package/dist/hooks/use-is-hydrated.js +20 -0
- package/dist/hooks/use-minute-clock.d.ts +23 -0
- package/dist/hooks/use-minute-clock.js +147 -0
- package/dist/hooks/use-month-transition-direction.d.ts +21 -0
- package/dist/hooks/use-month-transition-direction.js +110 -0
- package/dist/hooks/use-rich-link-editor.d.ts +55 -0
- package/dist/hooks/use-rich-link-editor.js +515 -0
- package/dist/hooks/use-viewer-time-zone.d.ts +6 -0
- package/dist/hooks/use-viewer-time-zone.js +20 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +43 -0
- package/dist/lib/browser-clipboard.d.ts +13 -0
- package/dist/lib/browser-clipboard.js +48 -0
- package/dist/lib/browser-navigation.d.ts +21 -0
- package/dist/lib/browser-navigation.js +42 -0
- package/dist/lib/file-acceptance.d.ts +43 -0
- package/dist/lib/file-acceptance.js +107 -0
- package/dist/lib/format-file-size.d.ts +16 -0
- package/dist/lib/format-file-size.js +34 -0
- package/dist/lib/fuzzy-search.d.ts +17 -0
- package/dist/lib/fuzzy-search.js +183 -0
- package/dist/lib/horizontal-swipe.d.ts +23 -0
- package/dist/lib/horizontal-swipe.js +29 -0
- package/dist/lib/in-app-browser.d.ts +35 -0
- package/dist/lib/in-app-browser.js +80 -0
- package/dist/lib/month-grid.d.ts +42 -0
- package/dist/lib/month-grid.js +74 -0
- package/dist/lib/name-initials.d.ts +7 -0
- package/dist/lib/name-initials.js +11 -0
- package/dist/lib/rich-text/link-markdown-constants.d.ts +147 -0
- package/dist/lib/rich-text/link-markdown-constants.js +458 -0
- package/dist/lib/rich-text/link-markdown-types.d.ts +53 -0
- package/dist/lib/rich-text/link-markdown-types.js +1 -0
- package/dist/lib/rich-text/link-markdown.d.ts +193 -0
- package/dist/lib/rich-text/link-markdown.js +592 -0
- package/dist/lib/rich-text/rich-markdown.d.ts +41 -0
- package/dist/lib/rich-text/rich-markdown.js +105 -0
- package/dist/lib/rich-text/rich-text-editor-dom.d.ts +26 -0
- package/dist/lib/rich-text/rich-text-editor-dom.js +79 -0
- package/dist/motion/tokens.d.ts +17 -1
- package/dist/motion/tokens.js +17 -1
- package/dist/styles.css +1 -1
- package/package.json +211 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure classification of a pointer drag into a horizontal swipe. Kept apart
|
|
3
|
+
* from the DOM so thresholds are unit-testable and shared by any pager.
|
|
4
|
+
*/
|
|
5
|
+
export const HORIZONTAL_SWIPE_DIRECTION = {
|
|
6
|
+
next: "next",
|
|
7
|
+
previous: "previous"
|
|
8
|
+
};
|
|
9
|
+
/** Minimum horizontal travel, in CSS pixels, to count as a swipe. */
|
|
10
|
+
const SWIPE_MIN_DISTANCE_PX = 60;
|
|
11
|
+
/** Horizontal travel must dominate vertical travel by this ratio. */
|
|
12
|
+
const SWIPE_HORIZONTAL_DOMINANCE_RATIO = 1.5;
|
|
13
|
+
/** Slower drags read as reading/scrolling, not as a page flip. */
|
|
14
|
+
const SWIPE_MAX_DURATION_MS = 800;
|
|
15
|
+
/**
|
|
16
|
+
* Decides whether a drag is a deliberate horizontal swipe. A leftward swipe
|
|
17
|
+
* moves to the next page and a rightward one to the previous page, like
|
|
18
|
+
* turning a calendar page.
|
|
19
|
+
*
|
|
20
|
+
* @param gesture - Pointer travel and duration.
|
|
21
|
+
* @returns The swipe direction, or null when the drag is not a swipe.
|
|
22
|
+
*/
|
|
23
|
+
export function resolveHorizontalSwipe(gesture) {
|
|
24
|
+
const horizontalDistance = Math.abs(gesture.deltaX);
|
|
25
|
+
if (horizontalDistance < SWIPE_MIN_DISTANCE_PX || horizontalDistance < Math.abs(gesture.deltaY) * SWIPE_HORIZONTAL_DOMINANCE_RATIO || gesture.durationMs > SWIPE_MAX_DURATION_MS) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return gesture.deltaX < 0 ? HORIZONTAL_SWIPE_DIRECTION.next : HORIZONTAL_SWIPE_DIRECTION.previous;
|
|
29
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Detects in-app browsers and builds deep links that reopen a URL in the default browser. */
|
|
2
|
+
export type InAppBrowserDetectionResult = {
|
|
3
|
+
isInAppBrowser: boolean;
|
|
4
|
+
isIos: boolean;
|
|
5
|
+
isAndroid: boolean;
|
|
6
|
+
isMercadoPago: boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Classifies a user agent: in-app browsers (social, messaging and payment apps, iOS web views)
|
|
10
|
+
* often block sign-in popups or lose sessions, so callers can offer to continue elsewhere.
|
|
11
|
+
* Safe on the server: pass the request `User-Agent` header.
|
|
12
|
+
*
|
|
13
|
+
* @param userAgent - User agent string, or nothing when unknown.
|
|
14
|
+
* @returns Whether it is an in-app browser and which platform it runs on.
|
|
15
|
+
*/
|
|
16
|
+
export declare function detectInAppBrowser(userAgent: string | null | undefined): InAppBrowserDetectionResult;
|
|
17
|
+
export declare const EXTERNAL_BROWSER_PLATFORM: {
|
|
18
|
+
readonly android: "android";
|
|
19
|
+
readonly ios: "ios";
|
|
20
|
+
};
|
|
21
|
+
export type ExternalBrowserPlatform = (typeof EXTERNAL_BROWSER_PLATFORM)[keyof typeof EXTERNAL_BROWSER_PLATFORM];
|
|
22
|
+
type BuildExternalBrowserUrlInput = {
|
|
23
|
+
platform: ExternalBrowserPlatform;
|
|
24
|
+
targetHttpsUrl: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Builds a deep link that asks the OS to open the given https URL in the
|
|
28
|
+
* user's default external browser instead of the in-app browser.
|
|
29
|
+
*
|
|
30
|
+
* @param input - Target URL and detected platform.
|
|
31
|
+
* @returns A deep-link URL for iOS Safari or Android Chrome. Returns null when
|
|
32
|
+
* the target URL does not use the https scheme.
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildExternalBrowserUrl({ platform, targetHttpsUrl, }: BuildExternalBrowserUrlInput): string | null;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** Detects in-app browsers and builds deep links that reopen a URL in the default browser. */
|
|
2
|
+
/** User-agent fragments of apps that open links in their own embedded browser. */
|
|
3
|
+
const IN_APP_BROWSER_USER_AGENT_TOKENS = [
|
|
4
|
+
"MercadoPago",
|
|
5
|
+
"MercadoLibre",
|
|
6
|
+
"MLWebKit",
|
|
7
|
+
"FBAN",
|
|
8
|
+
"FBAV",
|
|
9
|
+
"Instagram",
|
|
10
|
+
"Line/",
|
|
11
|
+
"MicroMessenger",
|
|
12
|
+
"Twitter"
|
|
13
|
+
];
|
|
14
|
+
const IOS_DEVICE_TOKENS = [
|
|
15
|
+
"iPhone",
|
|
16
|
+
"iPad",
|
|
17
|
+
"iPod"
|
|
18
|
+
];
|
|
19
|
+
const ANDROID_DEVICE_TOKEN = "Android";
|
|
20
|
+
const SAFARI_USER_AGENT_TOKEN = "Safari/";
|
|
21
|
+
const IOS_MOBILE_USER_AGENT_TOKEN = "Mobile/";
|
|
22
|
+
const MERCADO_PAGO_USER_AGENT_TOKENS = [
|
|
23
|
+
"MercadoPago",
|
|
24
|
+
"MercadoLibre",
|
|
25
|
+
"MLWebKit"
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* Classifies a user agent: in-app browsers (social, messaging and payment apps, iOS web views)
|
|
29
|
+
* often block sign-in popups or lose sessions, so callers can offer to continue elsewhere.
|
|
30
|
+
* Safe on the server: pass the request `User-Agent` header.
|
|
31
|
+
*
|
|
32
|
+
* @param userAgent - User agent string, or nothing when unknown.
|
|
33
|
+
* @returns Whether it is an in-app browser and which platform it runs on.
|
|
34
|
+
*/
|
|
35
|
+
export function detectInAppBrowser(userAgent) {
|
|
36
|
+
if (!userAgent) {
|
|
37
|
+
return {
|
|
38
|
+
isInAppBrowser: false,
|
|
39
|
+
isIos: false,
|
|
40
|
+
isAndroid: false,
|
|
41
|
+
isMercadoPago: false
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const isIos = IOS_DEVICE_TOKENS.some((token) => userAgent.includes(token));
|
|
45
|
+
const isAndroid = userAgent.includes(ANDROID_DEVICE_TOKEN);
|
|
46
|
+
const isMercadoPago = MERCADO_PAGO_USER_AGENT_TOKENS.some((token) => userAgent.includes(token));
|
|
47
|
+
const hasInAppToken = IN_APP_BROWSER_USER_AGENT_TOKENS.some((token) => userAgent.includes(token));
|
|
48
|
+
const isIosWebView = isIos && userAgent.includes(IOS_MOBILE_USER_AGENT_TOKEN) && !userAgent.includes(SAFARI_USER_AGENT_TOKEN);
|
|
49
|
+
return {
|
|
50
|
+
isInAppBrowser: hasInAppToken || isIosWebView,
|
|
51
|
+
isIos,
|
|
52
|
+
isAndroid,
|
|
53
|
+
isMercadoPago
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const HTTPS_PROTOCOL = "https://";
|
|
57
|
+
const SAFARI_URL_SCHEME = "x-safari-https://";
|
|
58
|
+
const CHROME_ANDROID_NAVIGATION_URL_PREFIX = "googlechrome://navigate?url=";
|
|
59
|
+
export const EXTERNAL_BROWSER_PLATFORM = {
|
|
60
|
+
android: "android",
|
|
61
|
+
ios: "ios"
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Builds a deep link that asks the OS to open the given https URL in the
|
|
65
|
+
* user's default external browser instead of the in-app browser.
|
|
66
|
+
*
|
|
67
|
+
* @param input - Target URL and detected platform.
|
|
68
|
+
* @returns A deep-link URL for iOS Safari or Android Chrome. Returns null when
|
|
69
|
+
* the target URL does not use the https scheme.
|
|
70
|
+
*/
|
|
71
|
+
export function buildExternalBrowserUrl({ platform, targetHttpsUrl }) {
|
|
72
|
+
if (!targetHttpsUrl.startsWith(HTTPS_PROTOCOL)) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const urlWithoutScheme = targetHttpsUrl.slice(HTTPS_PROTOCOL.length);
|
|
76
|
+
if (platform === EXTERNAL_BROWSER_PLATFORM.ios) {
|
|
77
|
+
return SAFARI_URL_SCHEME + urlWithoutScheme;
|
|
78
|
+
}
|
|
79
|
+
return CHROME_ANDROID_NAVIGATION_URL_PREFIX + encodeURIComponent(targetHttpsUrl);
|
|
80
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, time-zone-free helpers for month calendars. Days are `YYYY-MM-DD` keys and months are
|
|
3
|
+
* `YYYY-MM` keys; the application decides which zone turns an instant into a day key.
|
|
4
|
+
*/
|
|
5
|
+
export declare const CALENDAR_WEEK_LENGTH = 7;
|
|
6
|
+
/** First day of the week: 0 is Sunday, 1 is Monday. */
|
|
7
|
+
export type CalendarWeekStart = 0 | 1;
|
|
8
|
+
/** One cell of the month grid. */
|
|
9
|
+
export interface MonthGridDay {
|
|
10
|
+
/** `YYYY-MM-DD` key of the cell. */
|
|
11
|
+
dateKey: string;
|
|
12
|
+
dayNumber: number;
|
|
13
|
+
/** False for padding days of the neighbour months. */
|
|
14
|
+
isCurrentMonth: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Builds the cells of a month grid, padding the first and last week with neighbour-month days.
|
|
18
|
+
* @param month - `YYYY-MM` month key.
|
|
19
|
+
* @param weekStartsOn - First day of the week; Monday by default.
|
|
20
|
+
* @returns Every cell of the grid, a multiple of seven.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createMonthGridDays(month: string, weekStartsOn?: CalendarWeekStart): MonthGridDay[];
|
|
23
|
+
/**
|
|
24
|
+
* Splits grid cells into rows of seven days.
|
|
25
|
+
* @param days - Cells returned by {@link createMonthGridDays}.
|
|
26
|
+
* @returns One array per week.
|
|
27
|
+
*/
|
|
28
|
+
export declare function splitCalendarWeeks<TDay>(days: readonly TDay[]): TDay[][];
|
|
29
|
+
/**
|
|
30
|
+
* Indexes items by their day key, keeping input order within each day.
|
|
31
|
+
* @param items - Items, usually sorted by start.
|
|
32
|
+
* @param getDateKey - Reads the `YYYY-MM-DD` key of an item.
|
|
33
|
+
* @returns Record from day key to the items of that day.
|
|
34
|
+
*/
|
|
35
|
+
export declare function groupItemsByDateKey<TItem>(items: readonly TItem[], getDateKey: (item: TItem) => string): Record<string, TItem[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Moves a month key by a number of months.
|
|
38
|
+
* @param month - `YYYY-MM` month key.
|
|
39
|
+
* @param monthOffset - Months to add; negative values go back.
|
|
40
|
+
* @returns The resulting `YYYY-MM` key.
|
|
41
|
+
*/
|
|
42
|
+
export declare function shiftMonthKey(month: string, monthOffset: number): string;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, time-zone-free helpers for month calendars. Days are `YYYY-MM-DD` keys and months are
|
|
3
|
+
* `YYYY-MM` keys; the application decides which zone turns an instant into a day key.
|
|
4
|
+
*/
|
|
5
|
+
export const CALENDAR_WEEK_LENGTH = 7;
|
|
6
|
+
const MILLISECONDS_PER_DAY = 864e5;
|
|
7
|
+
const DATE_KEY_LENGTH = 10;
|
|
8
|
+
const MONTH_KEY_LENGTH = 7;
|
|
9
|
+
const MONTH_KEY_PATTERN = /^(\d{4})-(0[1-9]|1[0-2])$/;
|
|
10
|
+
/**
|
|
11
|
+
* Parses a `YYYY-MM` key.
|
|
12
|
+
* @param month - Month key.
|
|
13
|
+
* @returns The year and zero-based month index.
|
|
14
|
+
* @throws {RangeError} When the key is not a valid `YYYY-MM` month.
|
|
15
|
+
*/
|
|
16
|
+
function parseMonthKey(month) {
|
|
17
|
+
const match = MONTH_KEY_PATTERN.exec(month);
|
|
18
|
+
if (!match) throw new RangeError(`month-grid:parseMonthKey expected a YYYY-MM month, received "${month.slice(0, 20)}"`);
|
|
19
|
+
return {
|
|
20
|
+
year: Number(match[1]),
|
|
21
|
+
monthIndex: Number(match[2]) - 1
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Builds the cells of a month grid, padding the first and last week with neighbour-month days.
|
|
26
|
+
* @param month - `YYYY-MM` month key.
|
|
27
|
+
* @param weekStartsOn - First day of the week; Monday by default.
|
|
28
|
+
* @returns Every cell of the grid, a multiple of seven.
|
|
29
|
+
*/
|
|
30
|
+
export function createMonthGridDays(month, weekStartsOn = 1) {
|
|
31
|
+
const { year, monthIndex } = parseMonthKey(month);
|
|
32
|
+
const monthStartTime = Date.UTC(year, monthIndex, 1);
|
|
33
|
+
const daysInMonth = new Date(Date.UTC(year, monthIndex + 1, 0)).getUTCDate();
|
|
34
|
+
const leadingDays = (new Date(monthStartTime).getUTCDay() + CALENDAR_WEEK_LENGTH - weekStartsOn) % CALENDAR_WEEK_LENGTH;
|
|
35
|
+
const totalCells = Math.ceil((leadingDays + daysInMonth) / CALENDAR_WEEK_LENGTH) * CALENDAR_WEEK_LENGTH;
|
|
36
|
+
const firstCellTime = monthStartTime - leadingDays * MILLISECONDS_PER_DAY;
|
|
37
|
+
return Array.from({ length: totalCells }, (_, dayIndex) => {
|
|
38
|
+
const date = new Date(firstCellTime + dayIndex * MILLISECONDS_PER_DAY);
|
|
39
|
+
return {
|
|
40
|
+
dateKey: date.toISOString().slice(0, DATE_KEY_LENGTH),
|
|
41
|
+
dayNumber: date.getUTCDate(),
|
|
42
|
+
isCurrentMonth: date.getUTCMonth() === monthIndex
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Splits grid cells into rows of seven days.
|
|
48
|
+
* @param days - Cells returned by {@link createMonthGridDays}.
|
|
49
|
+
* @returns One array per week.
|
|
50
|
+
*/
|
|
51
|
+
export function splitCalendarWeeks(days) {
|
|
52
|
+
return Array.from({ length: Math.ceil(days.length / CALENDAR_WEEK_LENGTH) }, (_, weekIndex) => days.slice(weekIndex * CALENDAR_WEEK_LENGTH, (weekIndex + 1) * CALENDAR_WEEK_LENGTH));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Indexes items by their day key, keeping input order within each day.
|
|
56
|
+
* @param items - Items, usually sorted by start.
|
|
57
|
+
* @param getDateKey - Reads the `YYYY-MM-DD` key of an item.
|
|
58
|
+
* @returns Record from day key to the items of that day.
|
|
59
|
+
*/
|
|
60
|
+
export function groupItemsByDateKey(items, getDateKey) {
|
|
61
|
+
const groupedItems = {};
|
|
62
|
+
for (const item of items) (groupedItems[getDateKey(item)] ??= []).push(item);
|
|
63
|
+
return groupedItems;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Moves a month key by a number of months.
|
|
67
|
+
* @param month - `YYYY-MM` month key.
|
|
68
|
+
* @param monthOffset - Months to add; negative values go back.
|
|
69
|
+
* @returns The resulting `YYYY-MM` key.
|
|
70
|
+
*/
|
|
71
|
+
export function shiftMonthKey(month, monthOffset) {
|
|
72
|
+
const { year, monthIndex } = parseMonthKey(month);
|
|
73
|
+
return new Date(Date.UTC(year, monthIndex + monthOffset, 1)).toISOString().slice(0, MONTH_KEY_LENGTH);
|
|
74
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Builds avatar initials from display names, shared by account and member avatars. */
|
|
2
|
+
/**
|
|
3
|
+
* Builds up to two uppercase initials from a display name, ignoring repeated spaces.
|
|
4
|
+
* @param name - Display name, possibly blank.
|
|
5
|
+
* @returns The initials, or an empty string for a blank name.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getNameInitials(name: string): string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Builds avatar initials from display names, shared by account and member avatars. */
|
|
2
|
+
const NAME_PART_SEPARATOR = /\s+/;
|
|
3
|
+
const MAX_INITIALS = 2;
|
|
4
|
+
/**
|
|
5
|
+
* Builds up to two uppercase initials from a display name, ignoring repeated spaces.
|
|
6
|
+
* @param name - Display name, possibly blank.
|
|
7
|
+
* @returns The initials, or an empty string for a blank name.
|
|
8
|
+
*/
|
|
9
|
+
export function getNameInitials(name) {
|
|
10
|
+
return name.trim().split(NAME_PART_SEPARATOR).filter(Boolean).slice(0, MAX_INITIALS).map((namePart) => namePart.charAt(0).toUpperCase()).join("");
|
|
11
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared constants for the rich-text link layer.
|
|
3
|
+
*
|
|
4
|
+
* These values back both the read-only renderer (`RichTextContent`) and the
|
|
5
|
+
* rich link editor (`RichLinkEditor` / `useRichLinkEditor`). They are framework
|
|
6
|
+
* safe and free of business rules, so they live under `lib` and can be reused by
|
|
7
|
+
* any feature that needs autolinking plus `[text](url)` markdown support.
|
|
8
|
+
*/
|
|
9
|
+
/** URL protocols accepted when normalizing a markdown link target. */
|
|
10
|
+
export declare const LINK_MARKDOWN_ALLOWED_PROTOCOL: {
|
|
11
|
+
readonly http: "http:";
|
|
12
|
+
readonly https: "https:";
|
|
13
|
+
};
|
|
14
|
+
/** Protocol prepended to bare domains that lack an explicit scheme. */
|
|
15
|
+
export declare const LINK_PROTOCOL_PREFIX: {
|
|
16
|
+
readonly default: "https://";
|
|
17
|
+
};
|
|
18
|
+
/** Tokens used to assemble and recognize `[text](url)` markdown links. */
|
|
19
|
+
export declare const LINK_MARKDOWN_FORMAT: {
|
|
20
|
+
readonly closeLabel: "]";
|
|
21
|
+
readonly closeUrl: ")";
|
|
22
|
+
readonly openLabel: "[";
|
|
23
|
+
readonly openUrl: "](";
|
|
24
|
+
readonly suppressedUrl: "#";
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Parentheses that the `[text](url)` markdown URL group cannot represent
|
|
28
|
+
* unescaped, paired with their percent-encoded equivalents. The URL group only
|
|
29
|
+
* accepts balanced single-level `(...)` pairs, so any other paren must be
|
|
30
|
+
* encoded when serializing a link target; browsers decode `%28`/`%29` back to
|
|
31
|
+
* the literal character when the link is opened.
|
|
32
|
+
*/
|
|
33
|
+
export declare const LINK_MARKDOWN_URL_PAREN: {
|
|
34
|
+
readonly close: ")";
|
|
35
|
+
readonly encodedClose: "%29";
|
|
36
|
+
readonly encodedOpen: "%28";
|
|
37
|
+
readonly open: "(";
|
|
38
|
+
};
|
|
39
|
+
/** Patterns matching characters that must be escaped inside link label text. */
|
|
40
|
+
export declare const LINK_MARKDOWN_ESCAPE_PATTERN: {
|
|
41
|
+
readonly backslash: RegExp;
|
|
42
|
+
readonly closeLabel: RegExp;
|
|
43
|
+
readonly lineBreak: RegExp;
|
|
44
|
+
readonly openLabel: RegExp;
|
|
45
|
+
};
|
|
46
|
+
/** Escape sequences paired with `LINK_MARKDOWN_ESCAPE_PATTERN`. */
|
|
47
|
+
export declare const LINK_MARKDOWN_ESCAPE_VALUE: {
|
|
48
|
+
readonly backslash: "\\";
|
|
49
|
+
readonly escapedBackslash: "\\\\";
|
|
50
|
+
readonly escapedCloseLabel: "\\]";
|
|
51
|
+
readonly escapedLineBreak: "\\n";
|
|
52
|
+
readonly escapedOpenLabel: "\\[";
|
|
53
|
+
readonly lineBreakToken: "n";
|
|
54
|
+
};
|
|
55
|
+
/** Discriminant for the parsed rich-text segments (plain text vs. link). */
|
|
56
|
+
export declare const RICH_TEXT_SEGMENT_TYPE: {
|
|
57
|
+
readonly link: "link";
|
|
58
|
+
readonly text: "text";
|
|
59
|
+
};
|
|
60
|
+
/** Whether an editor link is explicit (`[text](url)`) or a suppressed autolink. */
|
|
61
|
+
export declare const RICH_LINK_KIND: {
|
|
62
|
+
readonly explicit: "explicit";
|
|
63
|
+
readonly suppressed: "suppressed";
|
|
64
|
+
};
|
|
65
|
+
/** Origin of a preview link segment: auto-detected URL vs. explicit link. */
|
|
66
|
+
export declare const RICH_PREVIEW_LINK_SOURCE: {
|
|
67
|
+
readonly automatic: "automatic";
|
|
68
|
+
readonly explicit: "explicit";
|
|
69
|
+
};
|
|
70
|
+
/** Editing modes of the link popover. */
|
|
71
|
+
export declare const RICH_LINK_POPOVER_MODE: {
|
|
72
|
+
readonly actions: "actions";
|
|
73
|
+
readonly edit: "edit";
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Regular expressions used to detect links in plain text.
|
|
77
|
+
*
|
|
78
|
+
* The URL character classes exclude both `)` and `]` so a URL wrapped in
|
|
79
|
+
* brackets or parentheses (`[https://example.com/path]`) stops at the closing
|
|
80
|
+
* delimiter instead of swallowing it into the link target. Balanced `(...)`
|
|
81
|
+
* inside the URL is still matched through the dedicated paren-balancing group.
|
|
82
|
+
*/
|
|
83
|
+
export declare const LINK_PATTERN: {
|
|
84
|
+
readonly bareDomain: RegExp;
|
|
85
|
+
readonly containsWhitespace: RegExp;
|
|
86
|
+
readonly bareUrl: RegExp;
|
|
87
|
+
readonly hostBoundary: RegExp;
|
|
88
|
+
readonly markdown: RegExp;
|
|
89
|
+
readonly protocolPrefix: RegExp;
|
|
90
|
+
readonly trailingPunctuation: RegExp;
|
|
91
|
+
readonly whitespace: RegExp;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Public top-level domains recognized when auto-linking a scheme-less bare
|
|
95
|
+
* domain found in prose. `new URL()` accepts any syntactically valid hostname,
|
|
96
|
+
* so without this allowlist Spanish abbreviations like `EE.UU.` or
|
|
97
|
+
* end-of-sentence transitions like `China.Por` would resolve to bogus hosts and
|
|
98
|
+
* become clickable links. It covers the common gTLDs plus every assigned
|
|
99
|
+
* two-letter ccTLD; unknown suffixes such as `uu` or `por` are intentionally
|
|
100
|
+
* absent. This gates only the automatic detection path: explicit `http(s)://`
|
|
101
|
+
* URLs and links the user pastes deliberately bypass it and stay permissive.
|
|
102
|
+
*/
|
|
103
|
+
export declare const KNOWN_TOP_LEVEL_DOMAINS: Set<string>;
|
|
104
|
+
/** Capture group indexes of `LINK_PATTERN.markdown`. */
|
|
105
|
+
export declare const LINK_MARKDOWN_MATCH_GROUP: {
|
|
106
|
+
readonly text: 1;
|
|
107
|
+
readonly url: 2;
|
|
108
|
+
};
|
|
109
|
+
/** Sentinel returned by the text diff when no change is found. */
|
|
110
|
+
export declare const TEXT_DIFF_FALLBACK_INDEX: {
|
|
111
|
+
readonly notFound: -1;
|
|
112
|
+
};
|
|
113
|
+
/** Separator used to build a stable key for preview link segments. */
|
|
114
|
+
export declare const PREVIEW_LINK_KEY_SEPARATOR: {
|
|
115
|
+
readonly value: ":";
|
|
116
|
+
};
|
|
117
|
+
/** Clipboard data type read on paste inside the editor. */
|
|
118
|
+
export declare const RICH_TEXT_CLIPBOARD_DATA_TYPE: {
|
|
119
|
+
readonly plainText: "text/plain";
|
|
120
|
+
};
|
|
121
|
+
/** `InputEvent.inputType` values handled by the editor's `beforeInput`. */
|
|
122
|
+
export declare const RICH_TEXT_EDITOR_INPUT_TYPE: {
|
|
123
|
+
readonly deleteContentBackward: "deleteContentBackward";
|
|
124
|
+
readonly deleteContentForward: "deleteContentForward";
|
|
125
|
+
readonly insertLineBreak: "insertLineBreak";
|
|
126
|
+
readonly insertParagraph: "insertParagraph";
|
|
127
|
+
readonly insertText: "insertText";
|
|
128
|
+
};
|
|
129
|
+
/** Keyboard keys handled by the editor's `keydown`. */
|
|
130
|
+
export declare const RICH_TEXT_EDITOR_KEY: {
|
|
131
|
+
readonly backspace: "Backspace";
|
|
132
|
+
readonly delete: "Delete";
|
|
133
|
+
readonly enter: "Enter";
|
|
134
|
+
};
|
|
135
|
+
/** Direction of a word-wise deletion. */
|
|
136
|
+
export declare const RICH_TEXT_EDITOR_WORD_DIRECTION: {
|
|
137
|
+
readonly backward: "backward";
|
|
138
|
+
readonly forward: "forward";
|
|
139
|
+
};
|
|
140
|
+
/** Length of a single editable character. */
|
|
141
|
+
export declare const RICH_TEXT_EDITOR_KEY_LENGTH: {
|
|
142
|
+
readonly character: 1;
|
|
143
|
+
};
|
|
144
|
+
/** Literal text tokens used by the editor. */
|
|
145
|
+
export declare const RICH_TEXT_EDITOR_TEXT: {
|
|
146
|
+
readonly lineBreak: "\n";
|
|
147
|
+
};
|