beez-ui 0.6.2 → 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 +150 -96
- package/README.md +33 -6
- package/dist/components/account-menu.d.ts +53 -0
- package/dist/components/account-menu.js +405 -0
- package/dist/components/alert.js +1 -1
- 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/avatar.d.ts +2 -1
- package/dist/components/avatar.js +4 -3
- package/dist/components/bouncing-dots-loader.d.ts +19 -0
- package/dist/components/bouncing-dots-loader.js +47 -0
- package/dist/components/calendar.js +102 -0
- package/dist/components/carousel.js +61 -48
- package/dist/components/checkbox.js +1 -1
- package/dist/components/confirm-delete-button.d.ts +29 -0
- package/dist/components/confirm-delete-button.js +254 -0
- package/dist/components/data-table.js +34 -4
- package/dist/components/dropdown-menu.js +5 -2
- 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/filter-query-bar.js +11 -6
- package/dist/components/form.d.ts +5 -0
- package/dist/components/form.js +11 -9
- package/dist/components/info-popover.d.ts +23 -0
- package/dist/components/info-popover.js +158 -0
- package/dist/components/input-group.js +1 -1
- package/dist/components/input.js +1 -1
- 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/pagination.d.ts +1 -0
- package/dist/components/pagination.js +9 -4
- 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/radio-group.js +1 -1
- 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/components/select.js +1 -1
- package/dist/components/sheet.js +12 -9
- package/dist/components/sidebar.js +9 -5
- package/dist/components/skeleton.d.ts +6 -0
- package/dist/components/skeleton.js +7 -1
- package/dist/components/switch.js +1 -1
- package/dist/components/textarea.js +1 -1
- package/dist/components/typing-animation.js +42 -15
- 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/column-filter-value.d.ts +12 -0
- package/dist/lib/column-filter-value.js +52 -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/glide-indicator.d.ts +37 -3
- package/dist/motion/glide-indicator.js +37 -3
- package/dist/motion/motion-slot.js +47 -18
- package/dist/motion/surface-keyframes.js +18 -6
- package/dist/motion/tokens.d.ts +41 -2
- package/dist/motion/tokens.js +40 -1
- package/dist/providers/beez-ui-provider.js +49 -17
- package/dist/styles.css +1 -1
- package/package.json +212 -2
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asks people inside an in-app browser to continue in their regular browser: a Safari deep link
|
|
3
|
+
* on iOS, manual steps elsewhere, and the destination URL ready to copy. Server-safe.
|
|
4
|
+
*/
|
|
5
|
+
import { cn } from "../lib/utils.js";
|
|
6
|
+
import { EXTERNAL_BROWSER_PLATFORM, buildExternalBrowserUrl } from "../lib/in-app-browser.js";
|
|
7
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
|
+
const ENTER_CLASS_NAME = "animate-in fade-in slide-in-from-bottom-1.5 fill-mode-both duration-300";
|
|
9
|
+
const OPEN_IN_BROWSER_DEFAULT_COPY = {
|
|
10
|
+
title: "Abrí este sitio en tu navegador",
|
|
11
|
+
description: "Estás dentro del navegador interno de la app que te trajo hasta acá. Para continuar, abrí este sitio en tu navegador habitual.",
|
|
12
|
+
iosButtonLabel: "Abrir en Safari",
|
|
13
|
+
iosHint: "Tocá el botón para abrir el sitio en Safari y continuá desde ahí.",
|
|
14
|
+
manualHint: "Tocá los tres puntos arriba a la derecha y elegí “Abrir en el navegador”, o copiá este link y pegalo en tu navegador."
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Renders the handoff section. The URL is selectable with one tap, since copying is the only
|
|
18
|
+
* universal escape from embedded browsers.
|
|
19
|
+
* @param props - Destination URL, platform and optional copy.
|
|
20
|
+
* @returns The call to action.
|
|
21
|
+
*/
|
|
22
|
+
export function OpenInBrowserCta({ url, isIos, copy, className }) {
|
|
23
|
+
const resolvedCopy = {
|
|
24
|
+
...OPEN_IN_BROWSER_DEFAULT_COPY,
|
|
25
|
+
...copy
|
|
26
|
+
};
|
|
27
|
+
const safariUrl = buildExternalBrowserUrl({
|
|
28
|
+
platform: EXTERNAL_BROWSER_PLATFORM.ios,
|
|
29
|
+
targetHttpsUrl: url
|
|
30
|
+
}) ?? url;
|
|
31
|
+
return /* @__PURE__ */ _jsxs("section", {
|
|
32
|
+
"data-slot": "open-in-browser-cta",
|
|
33
|
+
className: cn("mt-6 flex flex-col gap-4", className),
|
|
34
|
+
children: [
|
|
35
|
+
/* @__PURE__ */ _jsx("h2", {
|
|
36
|
+
className: cn("m-0 text-lg font-semibold text-foreground", ENTER_CLASS_NAME),
|
|
37
|
+
children: resolvedCopy.title
|
|
38
|
+
}),
|
|
39
|
+
/* @__PURE__ */ _jsx("p", {
|
|
40
|
+
className: cn("m-0 text-sm leading-6 text-muted-foreground", ENTER_CLASS_NAME, "delay-35"),
|
|
41
|
+
children: resolvedCopy.description
|
|
42
|
+
}),
|
|
43
|
+
isIos ? /* @__PURE__ */ _jsxs(_Fragment, { children: [/* @__PURE__ */ _jsx("a", {
|
|
44
|
+
className: cn("inline-flex h-10 items-center justify-center self-stretch rounded-lg bg-primary px-4 text-sm font-medium text-primary-foreground no-underline [-webkit-tap-highlight-color:transparent] transition-[background-color,box-shadow,scale] outline-none active:scale-[0.98] focus-visible:shadow-[0_0_0_2px_var(--background),0_0_0_4px_var(--ring)] [@media(hover:hover)]:hover:bg-primary/80", ENTER_CLASS_NAME, "delay-70"),
|
|
45
|
+
href: safariUrl,
|
|
46
|
+
rel: "noopener noreferrer",
|
|
47
|
+
children: resolvedCopy.iosButtonLabel
|
|
48
|
+
}), /* @__PURE__ */ _jsx("p", {
|
|
49
|
+
className: cn("m-0 text-[0.8125rem] leading-5 text-muted-foreground", ENTER_CLASS_NAME, "delay-105"),
|
|
50
|
+
children: resolvedCopy.iosHint
|
|
51
|
+
})] }) : /* @__PURE__ */ _jsx("p", {
|
|
52
|
+
className: cn("m-0 text-[0.8125rem] leading-5 text-muted-foreground", ENTER_CLASS_NAME, "delay-70"),
|
|
53
|
+
children: resolvedCopy.manualHint
|
|
54
|
+
}),
|
|
55
|
+
/* @__PURE__ */ _jsx("p", {
|
|
56
|
+
className: cn("m-0 rounded-lg border border-border bg-muted/50 p-3 font-mono text-xs leading-[1.125rem] break-all text-muted-foreground select-all", ENTER_CLASS_NAME, "delay-140"),
|
|
57
|
+
children: url
|
|
58
|
+
})
|
|
59
|
+
]
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -2,6 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { type LinkProps } from "./link.js";
|
|
3
3
|
import { Button } from "./button.js";
|
|
4
4
|
declare function Pagination({ className, ...props }: React.ComponentProps<"nav">): React.JSX.Element;
|
|
5
|
+
/** Lists page links; the current page's highlight glides to the newly selected page. */
|
|
5
6
|
declare function PaginationContent({ className, ...props }: React.ComponentProps<"ul">): React.JSX.Element;
|
|
6
7
|
declare function PaginationItem({ ...props }: React.ComponentProps<"li">): React.JSX.Element;
|
|
7
8
|
export type PaginationLinkProps = {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { c as _c } from "react/compiler-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Link } from "./link.js";
|
|
5
|
+
import { GlideSlot } from "../motion/glide-slot.js";
|
|
5
6
|
import { cn } from "../lib/utils.js";
|
|
6
7
|
import { Button } from "./button.js";
|
|
7
8
|
import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon } from "lucide-react";
|
|
@@ -44,6 +45,7 @@ function Pagination(t0) {
|
|
|
44
45
|
}
|
|
45
46
|
return t2;
|
|
46
47
|
}
|
|
48
|
+
/** Lists page links; the current page's highlight glides to the newly selected page. */
|
|
47
49
|
function PaginationContent(t0) {
|
|
48
50
|
const $ = _c(8);
|
|
49
51
|
let className;
|
|
@@ -67,10 +69,13 @@ function PaginationContent(t0) {
|
|
|
67
69
|
}
|
|
68
70
|
let t2;
|
|
69
71
|
if ($[5] !== props || $[6] !== t1) {
|
|
70
|
-
t2 = /* @__PURE__ */ _jsx(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
t2 = /* @__PURE__ */ _jsx(GlideSlot, {
|
|
73
|
+
preset: "pagination",
|
|
74
|
+
children: /* @__PURE__ */ _jsx("ul", {
|
|
75
|
+
"data-slot": "pagination-content",
|
|
76
|
+
className: t1,
|
|
77
|
+
...props
|
|
78
|
+
})
|
|
74
79
|
});
|
|
75
80
|
$[5] = props;
|
|
76
81
|
$[6] = t1;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Cross-fades between states of the same region, such as idle, loading and result. */
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
declare const PRESENCE_SWAP_ELEMENTS: {
|
|
4
|
+
readonly div: import("motion/react").ForwardRefComponent<HTMLDivElement, import("motion/react").HTMLMotionProps<"div">>;
|
|
5
|
+
readonly span: import("motion/react").ForwardRefComponent<HTMLSpanElement, import("motion/react").HTMLMotionProps<"span">>;
|
|
6
|
+
};
|
|
7
|
+
export interface PresenceSwapProps {
|
|
8
|
+
/** Changing the key plays the exit of the previous content and the entry of the new one. */
|
|
9
|
+
presenceKey: string;
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
as?: keyof typeof PRESENCE_SWAP_ELEMENTS;
|
|
12
|
+
className?: string;
|
|
13
|
+
/** `wait` swaps sequentially; `popLayout` overlaps them for inline content. */
|
|
14
|
+
mode?: "wait" | "popLayout";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Swaps keyed content with a short lift-and-fade; the first render is not animated.
|
|
18
|
+
* @param props - Key identifying the current state and its content.
|
|
19
|
+
* @returns The animated content.
|
|
20
|
+
*/
|
|
21
|
+
export declare function PresenceSwap({ presenceKey, children, as, className, mode }: PresenceSwapProps): import("react").JSX.Element;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { c as _c } from "react/compiler-runtime";
|
|
3
|
+
import { AnimatePresence, motion, useIsPresent } from "motion/react";
|
|
4
|
+
import { usePrefersReducedMotion } from "../hooks/use-prefers-reduced-motion.js";
|
|
5
|
+
import { cn } from "../lib/utils.js";
|
|
6
|
+
import { MOTION_CONTENT_DISTANCE, MOTION_EASE, MOTION_TIMING } from "../motion/tokens.js";
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
const PRESENCE_SWAP_ELEMENTS = {
|
|
9
|
+
div: motion.div,
|
|
10
|
+
span: motion.span
|
|
11
|
+
};
|
|
12
|
+
const INSTANT_TRANSITION = { duration: 0 };
|
|
13
|
+
/**
|
|
14
|
+
* One keyed state of the swap. While it animates out it turns inert and hidden
|
|
15
|
+
* from assistive technology, so stale controls cannot be clicked or announced.
|
|
16
|
+
* @param props - Element type, class name and content.
|
|
17
|
+
* @returns The animated state.
|
|
18
|
+
*/
|
|
19
|
+
function PresenceSwapItem(t0) {
|
|
20
|
+
const $ = _c(20);
|
|
21
|
+
const { as, className, children } = t0;
|
|
22
|
+
const MotionElement = PRESENCE_SWAP_ELEMENTS[as];
|
|
23
|
+
const isPresent = useIsPresent();
|
|
24
|
+
const shouldReduceMotion = usePrefersReducedMotion();
|
|
25
|
+
const distance = shouldReduceMotion ? 0 : MOTION_CONTENT_DISTANCE;
|
|
26
|
+
const t1 = isPresent ? undefined : true;
|
|
27
|
+
const t2 = !isPresent || undefined;
|
|
28
|
+
let t3;
|
|
29
|
+
if ($[0] !== distance) {
|
|
30
|
+
t3 = {
|
|
31
|
+
opacity: 0,
|
|
32
|
+
y: distance
|
|
33
|
+
};
|
|
34
|
+
$[0] = distance;
|
|
35
|
+
$[1] = t3;
|
|
36
|
+
} else {
|
|
37
|
+
t3 = $[1];
|
|
38
|
+
}
|
|
39
|
+
let t4;
|
|
40
|
+
if ($[2] !== shouldReduceMotion) {
|
|
41
|
+
t4 = shouldReduceMotion ? INSTANT_TRANSITION : {
|
|
42
|
+
duration: MOTION_TIMING.enter,
|
|
43
|
+
ease: MOTION_EASE
|
|
44
|
+
};
|
|
45
|
+
$[2] = shouldReduceMotion;
|
|
46
|
+
$[3] = t4;
|
|
47
|
+
} else {
|
|
48
|
+
t4 = $[3];
|
|
49
|
+
}
|
|
50
|
+
let t5;
|
|
51
|
+
if ($[4] !== t4) {
|
|
52
|
+
t5 = {
|
|
53
|
+
opacity: 1,
|
|
54
|
+
y: 0,
|
|
55
|
+
transition: t4
|
|
56
|
+
};
|
|
57
|
+
$[4] = t4;
|
|
58
|
+
$[5] = t5;
|
|
59
|
+
} else {
|
|
60
|
+
t5 = $[5];
|
|
61
|
+
}
|
|
62
|
+
const t6 = -distance;
|
|
63
|
+
let t7;
|
|
64
|
+
if ($[6] !== shouldReduceMotion) {
|
|
65
|
+
t7 = shouldReduceMotion ? INSTANT_TRANSITION : {
|
|
66
|
+
duration: MOTION_TIMING.exit,
|
|
67
|
+
ease: MOTION_EASE
|
|
68
|
+
};
|
|
69
|
+
$[6] = shouldReduceMotion;
|
|
70
|
+
$[7] = t7;
|
|
71
|
+
} else {
|
|
72
|
+
t7 = $[7];
|
|
73
|
+
}
|
|
74
|
+
let t8;
|
|
75
|
+
if ($[8] !== t6 || $[9] !== t7) {
|
|
76
|
+
t8 = {
|
|
77
|
+
opacity: 0,
|
|
78
|
+
y: t6,
|
|
79
|
+
transition: t7
|
|
80
|
+
};
|
|
81
|
+
$[8] = t6;
|
|
82
|
+
$[9] = t7;
|
|
83
|
+
$[10] = t8;
|
|
84
|
+
} else {
|
|
85
|
+
t8 = $[10];
|
|
86
|
+
}
|
|
87
|
+
let t9;
|
|
88
|
+
if ($[11] !== MotionElement || $[12] !== children || $[13] !== className || $[14] !== t1 || $[15] !== t2 || $[16] !== t3 || $[17] !== t5 || $[18] !== t8) {
|
|
89
|
+
t9 = /* @__PURE__ */ _jsx(MotionElement, {
|
|
90
|
+
"data-slot": "presence-swap",
|
|
91
|
+
"aria-hidden": t1,
|
|
92
|
+
inert: t2,
|
|
93
|
+
className,
|
|
94
|
+
initial: t3,
|
|
95
|
+
animate: t5,
|
|
96
|
+
exit: t8,
|
|
97
|
+
children
|
|
98
|
+
});
|
|
99
|
+
$[11] = MotionElement;
|
|
100
|
+
$[12] = children;
|
|
101
|
+
$[13] = className;
|
|
102
|
+
$[14] = t1;
|
|
103
|
+
$[15] = t2;
|
|
104
|
+
$[16] = t3;
|
|
105
|
+
$[17] = t5;
|
|
106
|
+
$[18] = t8;
|
|
107
|
+
$[19] = t9;
|
|
108
|
+
} else {
|
|
109
|
+
t9 = $[19];
|
|
110
|
+
}
|
|
111
|
+
return t9;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Swaps keyed content with a short lift-and-fade; the first render is not animated.
|
|
115
|
+
* @param props - Key identifying the current state and its content.
|
|
116
|
+
* @returns The animated content.
|
|
117
|
+
*/
|
|
118
|
+
export function PresenceSwap(t0) {
|
|
119
|
+
const $ = _c(11);
|
|
120
|
+
const { presenceKey, children, as: t1, className, mode: t2 } = t0;
|
|
121
|
+
const as = t1 === undefined ? "div" : t1;
|
|
122
|
+
const mode = t2 === undefined ? "wait" : t2;
|
|
123
|
+
const t3 = as === "span" && "inline-flex items-center";
|
|
124
|
+
let t4;
|
|
125
|
+
if ($[0] !== className || $[1] !== t3) {
|
|
126
|
+
t4 = cn("min-w-0", t3, className);
|
|
127
|
+
$[0] = className;
|
|
128
|
+
$[1] = t3;
|
|
129
|
+
$[2] = t4;
|
|
130
|
+
} else {
|
|
131
|
+
t4 = $[2];
|
|
132
|
+
}
|
|
133
|
+
let t5;
|
|
134
|
+
if ($[3] !== as || $[4] !== children || $[5] !== presenceKey || $[6] !== t4) {
|
|
135
|
+
t5 = /* @__PURE__ */ _jsx(PresenceSwapItem, {
|
|
136
|
+
as,
|
|
137
|
+
className: t4,
|
|
138
|
+
children
|
|
139
|
+
}, presenceKey);
|
|
140
|
+
$[3] = as;
|
|
141
|
+
$[4] = children;
|
|
142
|
+
$[5] = presenceKey;
|
|
143
|
+
$[6] = t4;
|
|
144
|
+
$[7] = t5;
|
|
145
|
+
} else {
|
|
146
|
+
t5 = $[7];
|
|
147
|
+
}
|
|
148
|
+
let t6;
|
|
149
|
+
if ($[8] !== mode || $[9] !== t5) {
|
|
150
|
+
t6 = /* @__PURE__ */ _jsx(AnimatePresence, {
|
|
151
|
+
mode,
|
|
152
|
+
initial: false,
|
|
153
|
+
children: t5
|
|
154
|
+
});
|
|
155
|
+
$[8] = mode;
|
|
156
|
+
$[9] = t5;
|
|
157
|
+
$[10] = t6;
|
|
158
|
+
} else {
|
|
159
|
+
t6 = $[10];
|
|
160
|
+
}
|
|
161
|
+
return t6;
|
|
162
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Compact circular progress indicator whose arc inherits the surrounding text color. */
|
|
2
|
+
import type { ComponentProps } from "react";
|
|
3
|
+
export interface ProgressRingProps extends Omit<ComponentProps<"svg">, "children"> {
|
|
4
|
+
/** Completion ratio in the `[0, 1]` range; values outside are clamped. */
|
|
5
|
+
fraction: number;
|
|
6
|
+
/** Rendered width and height in pixels. */
|
|
7
|
+
size?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Accessible name. Without it the ring is decorative (`aria-hidden`), which fits when an
|
|
10
|
+
* adjacent label such as `3 / 4` already conveys the value.
|
|
11
|
+
*/
|
|
12
|
+
label?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Draws a ring whose arc covers `fraction` of the circle. The arc uses `currentColor`, so the
|
|
16
|
+
* parent's text color (for example success or warning) styles it without extra props.
|
|
17
|
+
* @param props - Fraction, size, optional accessible name and SVG attributes.
|
|
18
|
+
* @returns The progress ring.
|
|
19
|
+
*/
|
|
20
|
+
export declare function ProgressRing({ fraction, size, label, className, ...svgProps }: ProgressRingProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
const PROGRESS_RING_VIEWBOX_SIZE = 18;
|
|
4
|
+
const PROGRESS_RING_CENTER = PROGRESS_RING_VIEWBOX_SIZE / 2;
|
|
5
|
+
const PROGRESS_RING_RADIUS = 7;
|
|
6
|
+
const PROGRESS_RING_STROKE_WIDTH = 2.5;
|
|
7
|
+
const PROGRESS_RING_CIRCUMFERENCE = 2 * Math.PI * PROGRESS_RING_RADIUS;
|
|
8
|
+
const DEFAULT_PROGRESS_RING_SIZE_PX = 18;
|
|
9
|
+
/**
|
|
10
|
+
* Draws a ring whose arc covers `fraction` of the circle. The arc uses `currentColor`, so the
|
|
11
|
+
* parent's text color (for example success or warning) styles it without extra props.
|
|
12
|
+
* @param props - Fraction, size, optional accessible name and SVG attributes.
|
|
13
|
+
* @returns The progress ring.
|
|
14
|
+
*/
|
|
15
|
+
export function ProgressRing({ fraction, size = DEFAULT_PROGRESS_RING_SIZE_PX, label, className, ...svgProps }) {
|
|
16
|
+
const clampedFraction = Math.min(Math.max(Number.isFinite(fraction) ? fraction : 0, 0), 1);
|
|
17
|
+
const dashOffset = PROGRESS_RING_CIRCUMFERENCE * (1 - clampedFraction);
|
|
18
|
+
const accessibilityProps = label ? {
|
|
19
|
+
role: "progressbar",
|
|
20
|
+
"aria-label": label,
|
|
21
|
+
"aria-valuemin": 0,
|
|
22
|
+
"aria-valuemax": 100,
|
|
23
|
+
"aria-valuenow": Math.round(clampedFraction * 100)
|
|
24
|
+
} : { "aria-hidden": true };
|
|
25
|
+
return /* @__PURE__ */ _jsxs("svg", {
|
|
26
|
+
"data-slot": "progress-ring",
|
|
27
|
+
...accessibilityProps,
|
|
28
|
+
...svgProps,
|
|
29
|
+
className: cn("shrink-0 -rotate-90", className),
|
|
30
|
+
height: size,
|
|
31
|
+
viewBox: `0 0 ${PROGRESS_RING_VIEWBOX_SIZE} ${PROGRESS_RING_VIEWBOX_SIZE}`,
|
|
32
|
+
width: size,
|
|
33
|
+
children: [/* @__PURE__ */ _jsx("circle", {
|
|
34
|
+
className: "stroke-border/70",
|
|
35
|
+
cx: PROGRESS_RING_CENTER,
|
|
36
|
+
cy: PROGRESS_RING_CENTER,
|
|
37
|
+
fill: "none",
|
|
38
|
+
r: PROGRESS_RING_RADIUS,
|
|
39
|
+
strokeWidth: PROGRESS_RING_STROKE_WIDTH
|
|
40
|
+
}), clampedFraction > 0 ? /* @__PURE__ */ _jsx("circle", {
|
|
41
|
+
"data-slot": "progress-ring-indicator",
|
|
42
|
+
cx: PROGRESS_RING_CENTER,
|
|
43
|
+
cy: PROGRESS_RING_CENTER,
|
|
44
|
+
fill: "none",
|
|
45
|
+
r: PROGRESS_RING_RADIUS,
|
|
46
|
+
stroke: "currentColor",
|
|
47
|
+
strokeDasharray: PROGRESS_RING_CIRCUMFERENCE,
|
|
48
|
+
strokeDashoffset: dashOffset,
|
|
49
|
+
strokeLinecap: "round",
|
|
50
|
+
strokeWidth: PROGRESS_RING_STROKE_WIDTH
|
|
51
|
+
}) : null]
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface PwaUpdateControlLabels {
|
|
2
|
+
/** Short notice next to the button; hidden on narrow screens. */
|
|
3
|
+
badge?: string;
|
|
4
|
+
action?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface PwaUpdateControlProps {
|
|
7
|
+
labels?: PwaUpdateControlLabels;
|
|
8
|
+
/** Message posted to the waiting worker so it activates. */
|
|
9
|
+
skipWaitingMessage?: unknown;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Renders the update notice and action once an update is waiting; nothing otherwise, including
|
|
14
|
+
* during server rendering and hydration.
|
|
15
|
+
* @param props - Optional labels, activation message and class name.
|
|
16
|
+
* @returns The update control, or nothing.
|
|
17
|
+
*/
|
|
18
|
+
export declare function PwaUpdateControl({ labels, skipWaitingMessage, className }: PwaUpdateControlProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* Offers to apply a waiting service worker update. It stays hidden until the browser reports a
|
|
4
|
+
* waiting worker, asks it to activate on demand and reloads once the new worker takes control.
|
|
5
|
+
*/
|
|
6
|
+
import { useEffect, useRef, useState } from "react";
|
|
7
|
+
import { RefreshCw } from "lucide-react";
|
|
8
|
+
import { useIsHydrated } from "../hooks/use-is-hydrated.js";
|
|
9
|
+
import { cn } from "../lib/utils.js";
|
|
10
|
+
import { Button } from "./button.js";
|
|
11
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
|
+
/** Message understood by Workbox and most hand-written workers to call `skipWaiting()`. */
|
|
13
|
+
const DEFAULT_SKIP_WAITING_MESSAGE = { type: "SKIP_WAITING" };
|
|
14
|
+
const INSTALLED_WORKER_STATE = "installed";
|
|
15
|
+
const PWA_UPDATE_DEFAULT_LABELS = {
|
|
16
|
+
badge: "Hay una nueva versión",
|
|
17
|
+
action: "Actualizar app"
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Tells whether the current page can talk to service workers.
|
|
21
|
+
* @returns Whether the Service Worker API is available.
|
|
22
|
+
*/
|
|
23
|
+
function canUseServiceWorker() {
|
|
24
|
+
return typeof window !== "undefined" && "serviceWorker" in navigator;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Reports a waiting worker now, or as soon as a newly found one finishes installing.
|
|
28
|
+
* @param registration - Registration of the current page.
|
|
29
|
+
* @param onWaiting - Called when an update is ready to activate.
|
|
30
|
+
* @returns A cleanup that stops watching.
|
|
31
|
+
*/
|
|
32
|
+
function watchForWaitingWorker(registration, onWaiting) {
|
|
33
|
+
if (registration.waiting) onWaiting();
|
|
34
|
+
const handleUpdateFound = () => {
|
|
35
|
+
const installingWorker = registration.installing;
|
|
36
|
+
if (!installingWorker) return;
|
|
37
|
+
installingWorker.addEventListener("statechange", () => {
|
|
38
|
+
// Without a controller this is the first install, not an update.
|
|
39
|
+
if (installingWorker.state === INSTALLED_WORKER_STATE && navigator.serviceWorker.controller) onWaiting();
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
registration.addEventListener("updatefound", handleUpdateFound);
|
|
43
|
+
return () => registration.removeEventListener("updatefound", handleUpdateFound);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Renders the update notice and action once an update is waiting; nothing otherwise, including
|
|
47
|
+
* during server rendering and hydration.
|
|
48
|
+
* @param props - Optional labels, activation message and class name.
|
|
49
|
+
* @returns The update control, or nothing.
|
|
50
|
+
*/
|
|
51
|
+
export function PwaUpdateControl({ labels, skipWaitingMessage = DEFAULT_SKIP_WAITING_MESSAGE, className }) {
|
|
52
|
+
const resolvedLabels = {
|
|
53
|
+
...PWA_UPDATE_DEFAULT_LABELS,
|
|
54
|
+
...labels
|
|
55
|
+
};
|
|
56
|
+
const isHydrated = useIsHydrated();
|
|
57
|
+
const [hasUpdateReady, setHasUpdateReady] = useState(false);
|
|
58
|
+
const [isApplyingUpdate, setIsApplyingUpdate] = useState(false);
|
|
59
|
+
const hasReloadedForControllerChange = useRef(false);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (!isHydrated || !canUseServiceWorker()) return;
|
|
62
|
+
let stopWatchingRegistration = () => {};
|
|
63
|
+
let isDisposed = false;
|
|
64
|
+
const markUpdateReady = () => {
|
|
65
|
+
if (!isDisposed) setHasUpdateReady(true);
|
|
66
|
+
};
|
|
67
|
+
const handleControllerChange = () => {
|
|
68
|
+
if (hasReloadedForControllerChange.current) return;
|
|
69
|
+
hasReloadedForControllerChange.current = true;
|
|
70
|
+
window.location.reload();
|
|
71
|
+
};
|
|
72
|
+
navigator.serviceWorker.addEventListener("controllerchange", handleControllerChange);
|
|
73
|
+
const attachUpdateWatcher = async () => {
|
|
74
|
+
const registration = await navigator.serviceWorker.getRegistration();
|
|
75
|
+
if (!registration || isDisposed) return;
|
|
76
|
+
stopWatchingRegistration = watchForWaitingWorker(registration, markUpdateReady);
|
|
77
|
+
// A failed check (offline, server error) keeps the current version without blocking the page.
|
|
78
|
+
await registration.update().catch(() => undefined);
|
|
79
|
+
if (registration.waiting) markUpdateReady();
|
|
80
|
+
};
|
|
81
|
+
void attachUpdateWatcher();
|
|
82
|
+
return () => {
|
|
83
|
+
isDisposed = true;
|
|
84
|
+
stopWatchingRegistration();
|
|
85
|
+
navigator.serviceWorker.removeEventListener("controllerchange", handleControllerChange);
|
|
86
|
+
};
|
|
87
|
+
}, [isHydrated]);
|
|
88
|
+
const applyUpdate = async () => {
|
|
89
|
+
setIsApplyingUpdate(true);
|
|
90
|
+
try {
|
|
91
|
+
const registration = await navigator.serviceWorker.getRegistration();
|
|
92
|
+
if (!registration) return;
|
|
93
|
+
if (!registration.waiting) await registration.update();
|
|
94
|
+
registration.waiting?.postMessage(skipWaitingMessage);
|
|
95
|
+
} catch (updateError) {
|
|
96
|
+
console.error("PwaUpdateControl:applyUpdate failed to activate the waiting service worker", updateError);
|
|
97
|
+
} finally {
|
|
98
|
+
setIsApplyingUpdate(false);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
if (!isHydrated || !canUseServiceWorker() || !hasUpdateReady) return null;
|
|
102
|
+
return /* @__PURE__ */ _jsxs("div", {
|
|
103
|
+
"data-slot": "pwa-update-control",
|
|
104
|
+
className: cn("inline-flex items-center gap-[0.45rem]", className),
|
|
105
|
+
children: [/* @__PURE__ */ _jsx("span", {
|
|
106
|
+
className: "hidden items-center rounded-full border border-[color-mix(in_srgb,var(--border)_84%,var(--primary)_16%)] bg-[color-mix(in_srgb,var(--muted)_90%,var(--primary)_10%)] px-2 py-[0.1rem] text-[0.72rem] leading-tight text-[color-mix(in_srgb,var(--foreground)_78%,var(--primary)_22%)] sm:inline-flex",
|
|
107
|
+
children: resolvedLabels.badge
|
|
108
|
+
}), /* @__PURE__ */ _jsxs(Button, {
|
|
109
|
+
disabled: isApplyingUpdate,
|
|
110
|
+
size: "sm",
|
|
111
|
+
type: "button",
|
|
112
|
+
onClick: () => void applyUpdate(),
|
|
113
|
+
children: [/* @__PURE__ */ _jsx(RefreshCw, {
|
|
114
|
+
"aria-hidden": "true",
|
|
115
|
+
className: cn(isApplyingUpdate && "animate-spin")
|
|
116
|
+
}), resolvedLabels.action]
|
|
117
|
+
})]
|
|
118
|
+
});
|
|
119
|
+
}
|
|
@@ -56,7 +56,7 @@ function RadioGroupItem(t0) {
|
|
|
56
56
|
}
|
|
57
57
|
let t1;
|
|
58
58
|
if ($[3] !== className) {
|
|
59
|
-
t1 = cn("group/radio-group-item peer relative flex aspect-square size-4 shrink-0 rounded-full border border-input outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary", className);
|
|
59
|
+
t1 = cn("group/radio-group-item peer relative flex aspect-square size-4 shrink-0 rounded-full border border-input transition-[color,background-color,border-color,box-shadow] outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary", className);
|
|
60
60
|
$[3] = className;
|
|
61
61
|
$[4] = t1;
|
|
62
62
|
} else {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toggle for likes and similar reactions: the icon pops when the viewer reacts and the counter
|
|
3
|
+
* rolls in the direction of the change. The (optimistic) reaction state is owned by the caller.
|
|
4
|
+
*/
|
|
5
|
+
import type { ComponentType, MouseEvent, SVGProps } from "react";
|
|
6
|
+
export interface ReactionButtonProps {
|
|
7
|
+
/** Accessible name, including the current count (for example "Me gusta 3"). */
|
|
8
|
+
ariaLabel: string;
|
|
9
|
+
isActive: boolean;
|
|
10
|
+
count: number;
|
|
11
|
+
onClick: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
12
|
+
isDisabled?: boolean;
|
|
13
|
+
/** Icon of the reaction. While active it is filled with the destructive color; override it with `[&_svg]:` classes. */
|
|
14
|
+
icon?: ComponentType<SVGProps<SVGSVGElement>>;
|
|
15
|
+
/** Formats the visible count, for example with a locale-aware compact format. */
|
|
16
|
+
formatCount?: (count: number) => string;
|
|
17
|
+
className?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Renders an outline toggle button exposing its state with `aria-pressed`. The icon starts at
|
|
21
|
+
* rest (`initial={false}`), so server-rendered and already-active reactions never pop on load;
|
|
22
|
+
* only a change to active does, and not at all with reduced motion.
|
|
23
|
+
* @param props - Accessible name, state, count, click handler and optional icon.
|
|
24
|
+
* @returns The reaction button.
|
|
25
|
+
*/
|
|
26
|
+
export declare function ReactionButton({ ariaLabel, isActive, count, onClick, isDisabled, icon: Icon, formatCount, className, }: ReactionButtonProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { c as _c } from "react/compiler-runtime";
|
|
3
|
+
import { HeartIcon } from "lucide-react";
|
|
4
|
+
import { motion } from "motion/react";
|
|
5
|
+
import { usePrefersReducedMotion } from "../hooks/use-prefers-reduced-motion.js";
|
|
6
|
+
import { cn } from "../lib/utils.js";
|
|
7
|
+
import { MOTION_EASE, MOTION_TIMING } from "../motion/tokens.js";
|
|
8
|
+
import { AnimatedCount } from "./animated-count.js";
|
|
9
|
+
import { Button } from "./button.js";
|
|
10
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
+
/** Peak scale the icon reaches while popping into the active state. */
|
|
12
|
+
const REACTION_ICON_POP_SCALE = 1.28;
|
|
13
|
+
const REACTION_ICON_VARIANTS = {
|
|
14
|
+
idle: { scale: 1 },
|
|
15
|
+
active: {
|
|
16
|
+
scale: [
|
|
17
|
+
1,
|
|
18
|
+
REACTION_ICON_POP_SCALE,
|
|
19
|
+
1
|
|
20
|
+
],
|
|
21
|
+
transition: {
|
|
22
|
+
duration: MOTION_TIMING.pop,
|
|
23
|
+
ease: MOTION_EASE
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Renders an outline toggle button exposing its state with `aria-pressed`. The icon starts at
|
|
29
|
+
* rest (`initial={false}`), so server-rendered and already-active reactions never pop on load;
|
|
30
|
+
* only a change to active does, and not at all with reduced motion.
|
|
31
|
+
* @param props - Accessible name, state, count, click handler and optional icon.
|
|
32
|
+
* @returns The reaction button.
|
|
33
|
+
*/
|
|
34
|
+
export function ReactionButton(t0) {
|
|
35
|
+
const $ = _c(19);
|
|
36
|
+
const { ariaLabel, isActive, count, onClick, isDisabled: t1, icon: t2, formatCount, className } = t0;
|
|
37
|
+
const isDisabled = t1 === undefined ? false : t1;
|
|
38
|
+
const Icon = t2 === undefined ? HeartIcon : t2;
|
|
39
|
+
const shouldReduceMotion = usePrefersReducedMotion();
|
|
40
|
+
const t3 = isActive || undefined;
|
|
41
|
+
let t4;
|
|
42
|
+
if ($[0] !== className) {
|
|
43
|
+
t4 = cn("group/reaction", className);
|
|
44
|
+
$[0] = className;
|
|
45
|
+
$[1] = t4;
|
|
46
|
+
} else {
|
|
47
|
+
t4 = $[1];
|
|
48
|
+
}
|
|
49
|
+
const t5 = isActive && !shouldReduceMotion ? "active" : "idle";
|
|
50
|
+
let t6;
|
|
51
|
+
if ($[2] !== Icon) {
|
|
52
|
+
t6 = /* @__PURE__ */ _jsx(Icon, { className: "size-4 transition-[fill,color] group-data-active/reaction:fill-current group-data-active/reaction:text-destructive" });
|
|
53
|
+
$[2] = Icon;
|
|
54
|
+
$[3] = t6;
|
|
55
|
+
} else {
|
|
56
|
+
t6 = $[3];
|
|
57
|
+
}
|
|
58
|
+
let t7;
|
|
59
|
+
if ($[4] !== t5 || $[5] !== t6) {
|
|
60
|
+
t7 = /* @__PURE__ */ _jsx(motion.span, {
|
|
61
|
+
animate: t5,
|
|
62
|
+
"aria-hidden": true,
|
|
63
|
+
className: "inline-flex origin-center items-center justify-center",
|
|
64
|
+
initial: false,
|
|
65
|
+
variants: REACTION_ICON_VARIANTS,
|
|
66
|
+
children: t6
|
|
67
|
+
});
|
|
68
|
+
$[4] = t5;
|
|
69
|
+
$[5] = t6;
|
|
70
|
+
$[6] = t7;
|
|
71
|
+
} else {
|
|
72
|
+
t7 = $[6];
|
|
73
|
+
}
|
|
74
|
+
let t8;
|
|
75
|
+
if ($[7] !== count || $[8] !== formatCount) {
|
|
76
|
+
t8 = /* @__PURE__ */ _jsx(AnimatedCount, {
|
|
77
|
+
className: "min-w-[1ch]",
|
|
78
|
+
value: count,
|
|
79
|
+
format: formatCount
|
|
80
|
+
});
|
|
81
|
+
$[7] = count;
|
|
82
|
+
$[8] = formatCount;
|
|
83
|
+
$[9] = t8;
|
|
84
|
+
} else {
|
|
85
|
+
t8 = $[9];
|
|
86
|
+
}
|
|
87
|
+
let t9;
|
|
88
|
+
if ($[10] !== ariaLabel || $[11] !== isActive || $[12] !== isDisabled || $[13] !== onClick || $[14] !== t3 || $[15] !== t4 || $[16] !== t7 || $[17] !== t8) {
|
|
89
|
+
t9 = /* @__PURE__ */ _jsxs(Button, {
|
|
90
|
+
"data-slot": "reaction-button",
|
|
91
|
+
"data-active": t3,
|
|
92
|
+
"aria-label": ariaLabel,
|
|
93
|
+
"aria-pressed": isActive,
|
|
94
|
+
className: t4,
|
|
95
|
+
disabled: isDisabled,
|
|
96
|
+
onClick,
|
|
97
|
+
type: "button",
|
|
98
|
+
variant: "outline",
|
|
99
|
+
children: [t7, t8]
|
|
100
|
+
});
|
|
101
|
+
$[10] = ariaLabel;
|
|
102
|
+
$[11] = isActive;
|
|
103
|
+
$[12] = isDisabled;
|
|
104
|
+
$[13] = onClick;
|
|
105
|
+
$[14] = t3;
|
|
106
|
+
$[15] = t4;
|
|
107
|
+
$[16] = t7;
|
|
108
|
+
$[17] = t8;
|
|
109
|
+
$[18] = t9;
|
|
110
|
+
} else {
|
|
111
|
+
t9 = $[18];
|
|
112
|
+
}
|
|
113
|
+
return t9;
|
|
114
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { RichLinkEditorController } from "../hooks/use-rich-link-editor.js";
|
|
2
|
+
/** Copy for the link popover. Every entry is optional and defaults to Spanish. */
|
|
3
|
+
export type RichLinkEditorCopy = {
|
|
4
|
+
editAction: string;
|
|
5
|
+
editCancel: string;
|
|
6
|
+
editSave: string;
|
|
7
|
+
popoverTextLabel: string;
|
|
8
|
+
popoverUrlLabel: string;
|
|
9
|
+
removeAction: string;
|
|
10
|
+
};
|
|
11
|
+
export type RichLinkEditorProps = {
|
|
12
|
+
ariaLabel: string;
|
|
13
|
+
copy?: Partial<RichLinkEditorCopy>;
|
|
14
|
+
editor: RichLinkEditorController;
|
|
15
|
+
placeholder: string;
|
|
16
|
+
ariaDescribedBy?: string;
|
|
17
|
+
isDisabled?: boolean;
|
|
18
|
+
isInvalid?: boolean;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* `contentEditable` rich link editor: renders the controller's preview segments,
|
|
22
|
+
* turning each link into a popover that edits or removes it. All state lives in
|
|
23
|
+
* the `useRichLinkEditor` controller; this component is presentation only.
|
|
24
|
+
*/
|
|
25
|
+
export declare function RichLinkEditor({ ariaDescribedBy, ariaLabel, copy: copyOverrides, editor, isDisabled, isInvalid, placeholder, }: RichLinkEditorProps): import("react").JSX.Element;
|