@flowtomic/ui 0.1.4 → 0.1.5
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/components/atoms/actions/badge/badge.d.ts +2 -2
- package/dist/components/atoms/actions/button/button.d.ts +2 -2
- package/dist/components/atoms/data-display/index.d.ts +2 -0
- package/dist/components/atoms/data-display/index.d.ts.map +1 -1
- package/dist/components/atoms/data-display/index.js +1 -0
- package/dist/components/atoms/data-display/qr-code/index.d.ts +3 -0
- package/dist/components/atoms/data-display/qr-code/index.d.ts.map +1 -0
- package/dist/components/atoms/data-display/qr-code/index.js +1 -0
- package/dist/components/atoms/data-display/qr-code/qr-code.d.ts +18 -0
- package/dist/components/atoms/data-display/qr-code/qr-code.d.ts.map +1 -0
- package/dist/components/atoms/data-display/qr-code/qr-code.js +79 -0
- package/dist/components/atoms/feedback/alert/alert.d.ts +1 -1
- package/dist/components/atoms/feedback/alert-dialog/alert-dialog.d.ts +2 -2
- package/dist/components/atoms/forms/input/input.d.ts +2 -2
- package/dist/components/atoms/forms/toggle/toggle.d.ts +2 -2
- package/dist/components/atoms/layout/sidebar/sidebar.d.ts +2 -2
- package/dist/components/atoms/navigation/command/command.d.ts +13 -13
- package/dist/components/molecules/forms/item/item.d.ts +2 -2
- package/dist/components/molecules/forms/text-editor/index.d.ts +3 -0
- package/dist/components/molecules/forms/text-editor/index.d.ts.map +1 -0
- package/dist/components/molecules/forms/text-editor/index.js +1 -0
- package/dist/components/molecules/forms/text-editor/text-editor.d.ts +33 -0
- package/dist/components/molecules/forms/text-editor/text-editor.d.ts.map +1 -0
- package/dist/components/molecules/forms/text-editor/text-editor.js +211 -0
- package/dist/components/molecules/index.d.ts +2 -0
- package/dist/components/molecules/index.d.ts.map +1 -1
- package/dist/components/molecules/index.js +1 -0
- package/dist/components/organisms/context/context.d.ts +4 -4
- package/dist/components/organisms/document-editor/document-editor.d.ts +43 -0
- package/dist/components/organisms/document-editor/document-editor.d.ts.map +1 -0
- package/dist/components/organisms/document-editor/document-editor.js +144 -0
- package/dist/components/organisms/document-editor/index.d.ts +3 -0
- package/dist/components/organisms/document-editor/index.d.ts.map +1 -0
- package/dist/components/organisms/document-editor/index.js +1 -0
- package/dist/components/organisms/form-layout/form-layout.d.ts +111 -0
- package/dist/components/organisms/form-layout/form-layout.d.ts.map +1 -0
- package/dist/components/organisms/form-layout/form-layout.js +83 -0
- package/dist/components/organisms/form-layout/index.d.ts +2 -0
- package/dist/components/organisms/form-layout/index.d.ts.map +1 -0
- package/dist/components/organisms/form-layout/index.js +1 -0
- package/dist/components/organisms/index.d.ts +4 -0
- package/dist/components/organisms/index.d.ts.map +1 -1
- package/dist/components/organisms/index.js +2 -0
- package/dist/components/organisms/model-selector/model-selector.d.ts +1 -1
- package/dist/index.js +649 -514
- package/dist/styles/globals.css +489 -0
- package/dist/styles/theme.css +1364 -0
- package/dist/styles/typography.css +430 -0
- package/package.json +21 -5
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type VariantProps } from "class-variance-authority";
|
|
2
2
|
import React from "react";
|
|
3
3
|
declare const badgeVariants: (props?: ({
|
|
4
|
-
variant?: "
|
|
5
|
-
size?: "
|
|
4
|
+
variant?: "default" | "secondary" | "destructive" | "outline" | "success" | "warning" | "info" | null | undefined;
|
|
5
|
+
size?: "md" | "lg" | "sm" | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
7
|
export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
|
|
8
8
|
children: React.ReactNode;
|
|
@@ -2,8 +2,8 @@ import { type VariantProps } from "class-variance-authority";
|
|
|
2
2
|
import { type Transition } from "motion/react";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
declare const buttonVariants: (props?: ({
|
|
5
|
-
variant?: "link" | "
|
|
6
|
-
size?: "
|
|
5
|
+
variant?: "link" | "default" | "secondary" | "destructive" | "outline" | "success" | "info" | "ghost" | "natural" | null | undefined;
|
|
6
|
+
size?: "lg" | "default" | "sm" | "icon" | "icon-sm" | "icon-lg" | null | undefined;
|
|
7
7
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
8
8
|
export interface ButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "transition">, VariantProps<typeof buttonVariants> {
|
|
9
9
|
asChild?: boolean;
|
|
@@ -4,4 +4,6 @@ export type { CarouselApi, CarouselContentProps, CarouselItemProps, CarouselNext
|
|
|
4
4
|
export { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, useCarousel, } from "./carousel";
|
|
5
5
|
export type { ChartConfig, ChartContainerProps, ChartLegendContentProps, ChartStyleProps, ChartTooltipContentProps, } from "./chart";
|
|
6
6
|
export { ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, useChart, } from "./chart";
|
|
7
|
+
export type { QRCodeProps } from "./qr-code";
|
|
8
|
+
export { QRCode, qrCodeRootVariants } from "./qr-code";
|
|
7
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/atoms/data-display/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACzD,YAAY,EACV,WAAW,EACX,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,qBAAqB,EACrB,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,uBAAuB,EACvB,eAAe,EACf,wBAAwB,GACzB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,cAAc,EACd,WAAW,EACX,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,mBAAmB,EACnB,QAAQ,GACT,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/atoms/data-display/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACzD,YAAY,EACV,WAAW,EACX,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,qBAAqB,EACrB,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,QAAQ,EACR,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,uBAAuB,EACvB,eAAe,EACf,wBAAwB,GACzB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,cAAc,EACd,WAAW,EACX,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,mBAAmB,EACnB,QAAQ,GACT,MAAM,SAAS,CAAC;AAGjB,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { Calendar, CalendarDayButton } from "./calendar";
|
|
2
2
|
export { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, useCarousel, } from "./carousel";
|
|
3
3
|
export { ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, useChart, } from "./chart";
|
|
4
|
+
export { QRCode, qrCodeRootVariants } from "./qr-code";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/atoms/data-display/qr-code/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { QRCode, qrCodeRootVariants } from "./qr-code";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type Options as QRCodeStylingOptions } from "qr-code-styling";
|
|
3
|
+
import { type VariantProps } from "class-variance-authority";
|
|
4
|
+
declare const qrCodeRootVariants: (props?: ({
|
|
5
|
+
size?: "md" | "lg" | null | undefined;
|
|
6
|
+
animated?: boolean | null | undefined;
|
|
7
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
8
|
+
export interface QRCodeProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children">, VariantProps<typeof qrCodeRootVariants> {
|
|
9
|
+
/** Valor (data) codificado no QR. */
|
|
10
|
+
value: string;
|
|
11
|
+
/** Opções extras da lib qr-code-styling para customização avançada. */
|
|
12
|
+
options?: QRCodeStylingOptions;
|
|
13
|
+
/** Rótulo de acessibilidade (aria-label). */
|
|
14
|
+
ariaLabel?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare const QRCode: React.ForwardRefExoticComponent<QRCodeProps & React.RefAttributes<HTMLDivElement>>;
|
|
17
|
+
export { qrCodeRootVariants };
|
|
18
|
+
//# sourceMappingURL=qr-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qr-code.d.ts","sourceRoot":"","sources":["../../../../../src/components/atoms/data-display/qr-code/qr-code.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAsB,EAAE,KAAK,OAAO,IAAI,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACtF,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAIlE,QAAA,MAAM,kBAAkB;;;8EAkBvB,CAAC;AAEF,MAAM,WAAW,WACf,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,EAC5D,YAAY,CAAC,OAAO,kBAAkB,CAAC;IACzC,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA0CD,eAAO,MAAM,MAAM,oFA8ElB,CAAC;AAGF,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import QRCodeStyling from "qr-code-styling";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
// Size tokens (visual wrapper padding + internal QR size)
|
|
8
|
+
const qrCodeRootVariants = cva("relative flex items-center justify-center select-none", // base classes
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
size: {
|
|
12
|
+
md: "p-2",
|
|
13
|
+
lg: "p-3",
|
|
14
|
+
},
|
|
15
|
+
animated: {
|
|
16
|
+
true: "", // animation handled conditionally
|
|
17
|
+
false: "",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultVariants: {
|
|
21
|
+
size: "md",
|
|
22
|
+
animated: false,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
// Map sizes to pixel dimensions for QR render
|
|
26
|
+
const QR_SIZE = {
|
|
27
|
+
md: { width: 96, height: 96 },
|
|
28
|
+
lg: { width: 128, height: 128 },
|
|
29
|
+
};
|
|
30
|
+
// Corner frame handle
|
|
31
|
+
const FrameHandle = React.forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("size-3 rounded-tl border-t-2 border-l-2 border-[hsl(var(--primary))]", // use primary token
|
|
32
|
+
className), ...props })));
|
|
33
|
+
FrameHandle.displayName = "QRCodeFrameHandle";
|
|
34
|
+
// Animated gradient scan overlay (only when animated && !prefers-reduced-motion)
|
|
35
|
+
const GradientScan = React.forwardRef(({ className, style, ...props }, ref) => (_jsx("div", { ref: ref, "aria-hidden": true, className: cn("absolute bottom-0 h-1/2 w-full border-t border-[hsl(var(--primary))] bg-[hsl(var(--primary))/0.10]", "[mask-image:radial-gradient(52.19%_100%_at_50%_0%,_#000_0%,_rgba(0,0,0,0)_95.31%)]", "[webkit-mask-image:radial-gradient(52.19%_100%_at_50%_0%,_#000_0%,_rgba(0,0,0,0)_95.31%)]", className), style: style, ...props })));
|
|
36
|
+
GradientScan.displayName = "QRCodeGradientScan";
|
|
37
|
+
export const QRCode = React.forwardRef(({ value, options, size = "md", animated = false, className, ariaLabel = "QR code", ...props }, ref) => {
|
|
38
|
+
const containerRef = React.useRef(null);
|
|
39
|
+
const qrInstanceRef = React.useRef(null);
|
|
40
|
+
const mountedRef = React.useRef(false);
|
|
41
|
+
// Create instance once
|
|
42
|
+
React.useEffect(() => {
|
|
43
|
+
if (!containerRef.current || mountedRef.current)
|
|
44
|
+
return;
|
|
45
|
+
mountedRef.current = true;
|
|
46
|
+
const qrSize = size ?? "md";
|
|
47
|
+
qrInstanceRef.current = new QRCodeStyling({
|
|
48
|
+
width: QR_SIZE[qrSize].width,
|
|
49
|
+
height: QR_SIZE[qrSize].height,
|
|
50
|
+
data: value,
|
|
51
|
+
type: "svg",
|
|
52
|
+
// Sensible defaults (can be overridden via options)
|
|
53
|
+
margin: 0,
|
|
54
|
+
qrOptions: { typeNumber: 0, mode: "Byte", errorCorrectionLevel: "Q" },
|
|
55
|
+
imageOptions: { hideBackgroundDots: true, imageSize: 0.4 },
|
|
56
|
+
dotsOptions: { type: "rounded" },
|
|
57
|
+
backgroundOptions: { color: "transparent" },
|
|
58
|
+
...options,
|
|
59
|
+
});
|
|
60
|
+
qrInstanceRef.current.append(containerRef.current);
|
|
61
|
+
}, [options, size, value]);
|
|
62
|
+
// Update when value/options/size change
|
|
63
|
+
React.useEffect(() => {
|
|
64
|
+
if (!qrInstanceRef.current)
|
|
65
|
+
return;
|
|
66
|
+
const qrSize = size ?? "md";
|
|
67
|
+
qrInstanceRef.current.update({
|
|
68
|
+
data: value,
|
|
69
|
+
width: QR_SIZE[qrSize].width,
|
|
70
|
+
height: QR_SIZE[qrSize].height,
|
|
71
|
+
...options,
|
|
72
|
+
});
|
|
73
|
+
}, [value, options, size]);
|
|
74
|
+
// Respect prefers-reduced-motion for animated overlay
|
|
75
|
+
const prefersReducedMotion = React.useMemo(() => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches, []);
|
|
76
|
+
return (_jsxs("div", { ref: ref, role: "img", "aria-label": ariaLabel, className: cn(qrCodeRootVariants({ size, animated }), className), ...props, children: [_jsx("div", { ref: containerRef, className: "[&>canvas]:h-auto [&>canvas]:w-auto" }), _jsx(FrameHandle, { className: "absolute left-0 top-0" }), _jsx(FrameHandle, { className: "absolute top-0 right-0 rotate-90" }), _jsx(FrameHandle, { className: "absolute bottom-0 right-0 rotate-180" }), _jsx(FrameHandle, { className: "absolute bottom-0 left-0 -rotate-90" }), animated && !prefersReducedMotion && (_jsx(GradientScan, { className: "animate-[scan_2.4s_linear_infinite]", style: {} })), _jsx("style", { children: `@keyframes scan { from { transform: translateY(100%); opacity: .15; } to { transform: translateY(0%); opacity: .55; } }` })] }));
|
|
77
|
+
});
|
|
78
|
+
QRCode.displayName = "QRCode";
|
|
79
|
+
export { qrCodeRootVariants };
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import { type VariantProps } from "class-variance-authority";
|
|
15
15
|
import * as React from "react";
|
|
16
16
|
declare const alertVariants: (props?: ({
|
|
17
|
-
variant?: "default" | "
|
|
17
|
+
variant?: "default" | "destructive" | "success" | "warning" | "info" | null | undefined;
|
|
18
18
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
19
19
|
export interface AlertProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof alertVariants> {
|
|
20
20
|
}
|
|
@@ -19,7 +19,7 @@ export type AlertDialogTriggerProps = React.ComponentPropsWithoutRef<typeof Aler
|
|
|
19
19
|
* Variantes de animação para AlertDialogContent
|
|
20
20
|
*/
|
|
21
21
|
declare const alertDialogContentVariants: (props?: ({
|
|
22
|
-
animation?: "
|
|
22
|
+
animation?: "bottom" | "left" | "right" | "top" | "center" | "depth" | "3d" | null | undefined;
|
|
23
23
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
24
24
|
export type AlertDialogContentProps = React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content> & VariantProps<typeof alertDialogContentVariants> & {
|
|
25
25
|
/**
|
|
@@ -61,7 +61,7 @@ declare const AlertDialogOverlay: React.ForwardRefExoticComponent<Omit<AlertDial
|
|
|
61
61
|
* Suporta animações 3D quando animation="3d"
|
|
62
62
|
*/
|
|
63
63
|
declare const AlertDialogContent: React.ForwardRefExoticComponent<Omit<AlertDialogPrimitive.AlertDialogContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & VariantProps<(props?: ({
|
|
64
|
-
animation?: "
|
|
64
|
+
animation?: "bottom" | "left" | "right" | "top" | "center" | "depth" | "3d" | null | undefined;
|
|
65
65
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
|
|
66
66
|
/**
|
|
67
67
|
* Habilita backdrop blur (apenas para animation="3d")
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type VariantProps } from "class-variance-authority";
|
|
2
2
|
import React from "react";
|
|
3
3
|
declare const inputVariants: (props?: ({
|
|
4
|
-
size?: "
|
|
5
|
-
variant?: "default" | "
|
|
4
|
+
size?: "lg" | "default" | "sm" | null | undefined;
|
|
5
|
+
variant?: "default" | "success" | "error" | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
7
|
export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">, VariantProps<typeof inputVariants> {
|
|
8
8
|
label?: string;
|
|
@@ -2,8 +2,8 @@ import * as TogglePrimitive from "@radix-ui/react-toggle";
|
|
|
2
2
|
import { type VariantProps } from "class-variance-authority";
|
|
3
3
|
import type * as React from "react";
|
|
4
4
|
declare const toggleVariants: (props?: ({
|
|
5
|
-
variant?: "
|
|
6
|
-
size?: "
|
|
5
|
+
variant?: "default" | "outline" | null | undefined;
|
|
6
|
+
size?: "lg" | "default" | "sm" | null | undefined;
|
|
7
7
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
8
8
|
export interface ToggleProps extends React.ComponentProps<typeof TogglePrimitive.Root>, VariantProps<typeof toggleVariants> {
|
|
9
9
|
}
|
|
@@ -106,8 +106,8 @@ declare namespace SidebarMenuItem {
|
|
|
106
106
|
var displayName: string;
|
|
107
107
|
}
|
|
108
108
|
declare const sidebarMenuButtonVariants: (props?: ({
|
|
109
|
-
variant?: "
|
|
110
|
-
size?: "
|
|
109
|
+
variant?: "default" | "outline" | null | undefined;
|
|
110
|
+
size?: "lg" | "default" | "sm" | null | undefined;
|
|
111
111
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
112
112
|
export interface SidebarMenuButtonProps extends React.ComponentProps<"button">, VariantProps<typeof sidebarMenuButtonVariants> {
|
|
113
113
|
asChild?: boolean;
|
|
@@ -19,11 +19,11 @@ export type CommandSeparatorProps = React.ComponentPropsWithoutRef<typeof Comman
|
|
|
19
19
|
*/
|
|
20
20
|
declare const Command: React.ForwardRefExoticComponent<Omit<{
|
|
21
21
|
children?: React.ReactNode;
|
|
22
|
-
} & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>,
|
|
22
|
+
} & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
|
|
23
23
|
ref?: React.Ref<HTMLDivElement>;
|
|
24
24
|
} & {
|
|
25
25
|
asChild?: boolean;
|
|
26
|
-
},
|
|
26
|
+
}, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & {
|
|
27
27
|
label?: string;
|
|
28
28
|
shouldFilter?: boolean;
|
|
29
29
|
filter?: (value: string, search: string, keywords?: string[]) => number;
|
|
@@ -48,7 +48,7 @@ declare const CommandInput: React.ForwardRefExoticComponent<Omit<Omit<Pick<Pick<
|
|
|
48
48
|
ref?: React.Ref<HTMLInputElement>;
|
|
49
49
|
} & {
|
|
50
50
|
asChild?: boolean;
|
|
51
|
-
}, "
|
|
51
|
+
}, "asChild" | "key" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "onChange" | "value" | "type"> & {
|
|
52
52
|
value?: string;
|
|
53
53
|
onValueChange?: (search: string) => void;
|
|
54
54
|
} & React.RefAttributes<HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
|
|
@@ -57,11 +57,11 @@ declare const CommandInput: React.ForwardRefExoticComponent<Omit<Omit<Pick<Pick<
|
|
|
57
57
|
*/
|
|
58
58
|
declare const CommandList: React.ForwardRefExoticComponent<Omit<{
|
|
59
59
|
children?: React.ReactNode;
|
|
60
|
-
} & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>,
|
|
60
|
+
} & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
|
|
61
61
|
ref?: React.Ref<HTMLDivElement>;
|
|
62
62
|
} & {
|
|
63
63
|
asChild?: boolean;
|
|
64
|
-
},
|
|
64
|
+
}, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & {
|
|
65
65
|
label?: string;
|
|
66
66
|
} & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
67
67
|
/**
|
|
@@ -69,21 +69,21 @@ declare const CommandList: React.ForwardRefExoticComponent<Omit<{
|
|
|
69
69
|
*/
|
|
70
70
|
declare const CommandEmpty: React.ForwardRefExoticComponent<Omit<{
|
|
71
71
|
children?: React.ReactNode;
|
|
72
|
-
} & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>,
|
|
72
|
+
} & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
|
|
73
73
|
ref?: React.Ref<HTMLDivElement>;
|
|
74
74
|
} & {
|
|
75
75
|
asChild?: boolean;
|
|
76
|
-
},
|
|
76
|
+
}, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
77
77
|
/**
|
|
78
78
|
* CommandGroup - Grupo do command
|
|
79
79
|
*/
|
|
80
80
|
declare const CommandGroup: React.ForwardRefExoticComponent<Omit<{
|
|
81
81
|
children?: React.ReactNode;
|
|
82
|
-
} & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>,
|
|
82
|
+
} & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
|
|
83
83
|
ref?: React.Ref<HTMLDivElement>;
|
|
84
84
|
} & {
|
|
85
85
|
asChild?: boolean;
|
|
86
|
-
},
|
|
86
|
+
}, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "value" | "heading"> & {
|
|
87
87
|
heading?: React.ReactNode;
|
|
88
88
|
value?: string;
|
|
89
89
|
forceMount?: boolean;
|
|
@@ -91,11 +91,11 @@ declare const CommandGroup: React.ForwardRefExoticComponent<Omit<{
|
|
|
91
91
|
/**
|
|
92
92
|
* CommandSeparator - Separador do command
|
|
93
93
|
*/
|
|
94
|
-
declare const CommandSeparator: React.ForwardRefExoticComponent<Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>,
|
|
94
|
+
declare const CommandSeparator: React.ForwardRefExoticComponent<Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
|
|
95
95
|
ref?: React.Ref<HTMLDivElement>;
|
|
96
96
|
} & {
|
|
97
97
|
asChild?: boolean;
|
|
98
|
-
},
|
|
98
|
+
}, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & {
|
|
99
99
|
alwaysRender?: boolean;
|
|
100
100
|
} & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
101
101
|
/**
|
|
@@ -103,11 +103,11 @@ declare const CommandSeparator: React.ForwardRefExoticComponent<Omit<Pick<Pick<R
|
|
|
103
103
|
*/
|
|
104
104
|
declare const CommandItem: React.ForwardRefExoticComponent<Omit<{
|
|
105
105
|
children?: React.ReactNode;
|
|
106
|
-
} & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>,
|
|
106
|
+
} & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
|
|
107
107
|
ref?: React.Ref<HTMLDivElement>;
|
|
108
108
|
} & {
|
|
109
109
|
asChild?: boolean;
|
|
110
|
-
},
|
|
110
|
+
}, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "onSelect" | "value" | "disabled"> & {
|
|
111
111
|
disabled?: boolean;
|
|
112
112
|
onSelect?: (value: string) => void;
|
|
113
113
|
value?: string;
|
|
@@ -13,8 +13,8 @@ declare namespace ItemSeparator {
|
|
|
13
13
|
var displayName: string;
|
|
14
14
|
}
|
|
15
15
|
declare const itemVariants: (props?: ({
|
|
16
|
-
variant?: "
|
|
17
|
-
size?: "
|
|
16
|
+
variant?: "default" | "outline" | "muted" | null | undefined;
|
|
17
|
+
size?: "default" | "sm" | null | undefined;
|
|
18
18
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
19
19
|
export interface ItemProps extends React.ComponentProps<"div">, VariantProps<typeof itemVariants> {
|
|
20
20
|
asChild?: boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/molecules/forms/text-editor/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAC9F,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TextEditor } from "./text-editor";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TextEditor - Flowtomic UI
|
|
3
|
+
*
|
|
4
|
+
* Editor de texto rico com TipTap + modos opcionais Markdown e Preview.
|
|
5
|
+
* Regras de modo:
|
|
6
|
+
* - Somente 'rich' => sem abas, apenas editor rico
|
|
7
|
+
* - Somente 'markdown' => textarea markdown + (preview opcional)
|
|
8
|
+
* - Somente 'preview' => somente visualização
|
|
9
|
+
* - Múltiplos modos => abas para cada modo disponível
|
|
10
|
+
*/
|
|
11
|
+
import * as React from "react";
|
|
12
|
+
export type TextEditorMode = "rich" | "markdown" | "preview";
|
|
13
|
+
export type TextEditorToolbarAction = "bold" | "italic" | "strike" | "code" | "h1" | "h2" | "h3" | "bulletList" | "orderedList" | "blockquote" | "alignLeft" | "alignCenter" | "alignRight" | "textColor" | "image";
|
|
14
|
+
declare const DEFAULT_ACTIONS: TextEditorToolbarAction[];
|
|
15
|
+
type BaseDivProps = Omit<React.HTMLAttributes<HTMLDivElement>, "onChange">;
|
|
16
|
+
export interface TextEditorProps extends BaseDivProps {
|
|
17
|
+
value?: string;
|
|
18
|
+
defaultValue?: string;
|
|
19
|
+
onChange?: (value: string) => void;
|
|
20
|
+
placeholder?: string;
|
|
21
|
+
editable?: boolean;
|
|
22
|
+
mode?: TextEditorMode;
|
|
23
|
+
onModeChange?: (mode: TextEditorMode) => void;
|
|
24
|
+
toolbar?: boolean;
|
|
25
|
+
allowedActions?: TextEditorToolbarAction[];
|
|
26
|
+
onUploadImage?: (file: File) => Promise<string> | string;
|
|
27
|
+
outputFormat?: "markdown" | "text";
|
|
28
|
+
/** Modos disponíveis. 'markdown' sempre inclui 'preview' automaticamente */
|
|
29
|
+
availableModes?: ("rich" | "markdown")[];
|
|
30
|
+
}
|
|
31
|
+
export declare const TextEditor: React.ForwardRefExoticComponent<TextEditorProps & React.RefAttributes<HTMLDivElement>>;
|
|
32
|
+
export { DEFAULT_ACTIONS };
|
|
33
|
+
//# sourceMappingURL=text-editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-editor.d.ts","sourceRoot":"","sources":["../../../../../src/components/molecules/forms/text-editor/text-editor.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AA+C/B,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;AAE7D,MAAM,MAAM,uBAAuB,GAC/B,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,GACrC,IAAI,GAAG,IAAI,GAAG,IAAI,GAClB,YAAY,GAAG,aAAa,GAAG,YAAY,GAC3C,WAAW,GAAG,aAAa,GAAG,YAAY,GAC1C,WAAW,GACX,OAAO,CAAC;AAEZ,QAAA,MAAM,eAAe,EAAE,uBAAuB,EAgB7C,CAAC;AA2DF,KAAK,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,CAAC;AAE3E,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,uBAAuB,EAAE,CAAC;IAC3C,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IACzD,YAAY,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IACnC,4EAA4E;IAC5E,cAAc,CAAC,EAAE,CAAC,MAAM,GAAG,UAAU,CAAC,EAAE,CAAC;CAC1C;AAED,eAAO,MAAM,UAAU,wFAmUtB,CAAC;AA8BF,OAAO,EAAE,eAAe,EAAE,CAAC"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* TextEditor - Flowtomic UI
|
|
4
|
+
*
|
|
5
|
+
* Editor de texto rico com TipTap + modos opcionais Markdown e Preview.
|
|
6
|
+
* Regras de modo:
|
|
7
|
+
* - Somente 'rich' => sem abas, apenas editor rico
|
|
8
|
+
* - Somente 'markdown' => textarea markdown + (preview opcional)
|
|
9
|
+
* - Somente 'preview' => somente visualização
|
|
10
|
+
* - Múltiplos modos => abas para cada modo disponível
|
|
11
|
+
*/
|
|
12
|
+
import * as React from "react";
|
|
13
|
+
import { useEditor, EditorContent } from "@tiptap/react";
|
|
14
|
+
import StarterKit from "@tiptap/starter-kit";
|
|
15
|
+
import Placeholder from "@tiptap/extension-placeholder";
|
|
16
|
+
import TextStyle from "@tiptap/extension-text-style";
|
|
17
|
+
import Color from "@tiptap/extension-color";
|
|
18
|
+
import TextAlign from "@tiptap/extension-text-align";
|
|
19
|
+
import Image from "@tiptap/extension-image";
|
|
20
|
+
import { Markdown } from "tiptap-markdown";
|
|
21
|
+
import { Bold, Code, Heading1, Heading2, Heading3, ImageIcon, Italic, ListOrdered, List as ListIcon, Quote, Strikethrough, AlignLeft, AlignCenter, AlignRight, Eye, FileText, Palette, } from "lucide-react";
|
|
22
|
+
import { Streamdown } from "streamdown";
|
|
23
|
+
import { cn } from "@/lib/utils";
|
|
24
|
+
import { Button, Separator, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Popover, PopoverContent, PopoverTrigger, } from "../../../atoms";
|
|
25
|
+
const DEFAULT_ACTIONS = [
|
|
26
|
+
"bold",
|
|
27
|
+
"italic",
|
|
28
|
+
"strike",
|
|
29
|
+
"code",
|
|
30
|
+
"h1",
|
|
31
|
+
"h2",
|
|
32
|
+
"h3",
|
|
33
|
+
"bulletList",
|
|
34
|
+
"orderedList",
|
|
35
|
+
"blockquote",
|
|
36
|
+
"alignLeft",
|
|
37
|
+
"alignCenter",
|
|
38
|
+
"alignRight",
|
|
39
|
+
"textColor",
|
|
40
|
+
"image",
|
|
41
|
+
];
|
|
42
|
+
const PRESET_COLORS = [
|
|
43
|
+
{ label: "Preto", value: "#000000" },
|
|
44
|
+
{ label: "Cinza escuro", value: "#374151" },
|
|
45
|
+
{ label: "Cinza", value: "#6B7280" },
|
|
46
|
+
{ label: "Cinza claro", value: "#9CA3AF" },
|
|
47
|
+
{ label: "Vermelho", value: "#EF4444" },
|
|
48
|
+
{ label: "Laranja", value: "#F97316" },
|
|
49
|
+
{ label: "Amarelo", value: "#EAB308" },
|
|
50
|
+
{ label: "Verde", value: "#10B981" },
|
|
51
|
+
{ label: "Azul", value: "#3B82F6" },
|
|
52
|
+
{ label: "Índigo", value: "#6366F1" },
|
|
53
|
+
{ label: "Roxo", value: "#A855F7" },
|
|
54
|
+
{ label: "Rosa", value: "#EC4899" },
|
|
55
|
+
{ label: "Vermelho escuro", value: "#991B1B" },
|
|
56
|
+
{ label: "Laranja escuro", value: "#9A3412" },
|
|
57
|
+
{ label: "Amarelo escuro", value: "#854D0E" },
|
|
58
|
+
{ label: "Verde escuro", value: "#065F46" },
|
|
59
|
+
{ label: "Azul escuro", value: "#1E40AF" },
|
|
60
|
+
{ label: "Índigo escuro", value: "#3730A3" },
|
|
61
|
+
{ label: "Roxo escuro", value: "#6B21A8" },
|
|
62
|
+
{ label: "Rosa escuro", value: "#9F1239" },
|
|
63
|
+
{ label: "Vermelho claro", value: "#FCA5A5" },
|
|
64
|
+
{ label: "Laranja claro", value: "#FDBA74" },
|
|
65
|
+
{ label: "Amarelo claro", value: "#FDE047" },
|
|
66
|
+
{ label: "Verde claro", value: "#6EE7B7" },
|
|
67
|
+
{ label: "Azul claro", value: "#93C5FD" },
|
|
68
|
+
{ label: "Índigo claro", value: "#A5B4FC" },
|
|
69
|
+
{ label: "Roxo claro", value: "#D8B4FE" },
|
|
70
|
+
{ label: "Rosa claro", value: "#F9A8D4" },
|
|
71
|
+
];
|
|
72
|
+
function useMarkdownState(controlled, initial) {
|
|
73
|
+
const isControlled = controlled !== undefined;
|
|
74
|
+
const [value, setValue] = React.useState(initial);
|
|
75
|
+
React.useEffect(() => {
|
|
76
|
+
if (isControlled)
|
|
77
|
+
setValue(controlled);
|
|
78
|
+
}, [controlled, isControlled]);
|
|
79
|
+
return { value, setValue, isControlled };
|
|
80
|
+
}
|
|
81
|
+
function stripMarkdown(md) {
|
|
82
|
+
return md
|
|
83
|
+
.replace(/`{1,3}[^`]*`/g, (m) => m.replace(/`/g, ""))
|
|
84
|
+
.replace(/^#{1,6}\s+/gm, "")
|
|
85
|
+
.replace(/\*\*([^*]+)\*\*/g, "$1")
|
|
86
|
+
.replace(/\*([^*]+)\*/g, "$1")
|
|
87
|
+
.replace(/~~([^~]+)~~/g, "$1")
|
|
88
|
+
.replace(/>\s+/g, "")
|
|
89
|
+
.replace(/`([^`]+)`/g, "$1")
|
|
90
|
+
.replace(/^\s*[-*+]\s+/gm, "")
|
|
91
|
+
.replace(/^\s*\d+\.\s+/gm, "")
|
|
92
|
+
.replace(/!\[[^\]]*\]\([^)]*\)/g, "")
|
|
93
|
+
.replace(/\[[^\]]*\]\([^)]*\)/g, (m) => m.match(/\[([^\]]+)\]/)?.[1] || "")
|
|
94
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
95
|
+
.trim();
|
|
96
|
+
}
|
|
97
|
+
export const TextEditor = React.forwardRef(({ className, value: controlledValue, defaultValue, onChange, placeholder = "Escreva algo...", editable = true, mode: controlledMode, onModeChange, toolbar = true, allowedActions = DEFAULT_ACTIONS, onUploadImage, outputFormat = "markdown", availableModes = ["rich", "markdown"], ...props }, ref) => {
|
|
98
|
+
const initial = controlledValue ?? defaultValue ?? "";
|
|
99
|
+
const { value: internalMarkdown, setValue: setInternalMarkdown, isControlled } = useMarkdownState(controlledValue, initial);
|
|
100
|
+
// Expandir modos: se markdown está presente, adicionar preview automaticamente
|
|
101
|
+
const effectiveModes = React.useMemo(() => {
|
|
102
|
+
const unique = availableModes.filter((m, i, arr) => arr.indexOf(m) === i);
|
|
103
|
+
const expanded = [];
|
|
104
|
+
if (unique.includes("rich"))
|
|
105
|
+
expanded.push("rich");
|
|
106
|
+
if (unique.includes("markdown")) {
|
|
107
|
+
expanded.push("markdown");
|
|
108
|
+
expanded.push("preview"); // markdown sempre vem com preview
|
|
109
|
+
}
|
|
110
|
+
return expanded.length > 0 ? expanded : ["rich"];
|
|
111
|
+
}, [availableModes]);
|
|
112
|
+
const [mode, setMode] = React.useState(controlledMode && effectiveModes.includes(controlledMode) ? controlledMode : effectiveModes[0]);
|
|
113
|
+
const setModeSafe = React.useCallback((m) => {
|
|
114
|
+
if (!effectiveModes.includes(m))
|
|
115
|
+
return;
|
|
116
|
+
setMode(m);
|
|
117
|
+
onModeChange?.(m);
|
|
118
|
+
}, [effectiveModes, onModeChange]);
|
|
119
|
+
const editor = useEditor({
|
|
120
|
+
editable,
|
|
121
|
+
extensions: [
|
|
122
|
+
Markdown.configure({ html: false, transformPastedText: true, transformCopiedText: true }),
|
|
123
|
+
StarterKit.configure({ heading: { levels: [1, 2, 3] } }),
|
|
124
|
+
TextStyle,
|
|
125
|
+
Color,
|
|
126
|
+
TextAlign.configure({ types: ["heading", "paragraph"] }),
|
|
127
|
+
Placeholder.configure({ placeholder }),
|
|
128
|
+
Image,
|
|
129
|
+
],
|
|
130
|
+
content: internalMarkdown ? undefined : "",
|
|
131
|
+
onUpdate: ({ editor }) => {
|
|
132
|
+
try {
|
|
133
|
+
// @ts-expect-error acesso ao storage markdown da extensão
|
|
134
|
+
const md = editor?.storage?.markdown?.getMarkdown?.() ?? editor.commands.getMarkdown?.();
|
|
135
|
+
if (typeof md === "string") {
|
|
136
|
+
if (!isControlled)
|
|
137
|
+
setInternalMarkdown(md);
|
|
138
|
+
if (outputFormat === "text") {
|
|
139
|
+
onChange?.(editor.getText());
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
onChange?.(md);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
/* silencioso */
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
React.useEffect(() => {
|
|
152
|
+
if (!editor)
|
|
153
|
+
return;
|
|
154
|
+
if (controlledValue === undefined)
|
|
155
|
+
return;
|
|
156
|
+
const nextMarkdown = controlledValue; // já é markdown ou texto; se texto não temos conversão -> usar direto
|
|
157
|
+
// @ts-expect-error comando da extensão markdown
|
|
158
|
+
if (editor.commands?.setMarkdown) {
|
|
159
|
+
// @ts-expect-error
|
|
160
|
+
editor.commands.setMarkdown(nextMarkdown);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
editor.commands.setContent(nextMarkdown);
|
|
164
|
+
}
|
|
165
|
+
}, [editor, controlledValue]);
|
|
166
|
+
const handleImageInsert = React.useCallback(async () => {
|
|
167
|
+
if (!editor)
|
|
168
|
+
return;
|
|
169
|
+
const input = document.createElement("input");
|
|
170
|
+
input.type = "file";
|
|
171
|
+
input.accept = "image/*";
|
|
172
|
+
input.onchange = async () => {
|
|
173
|
+
const file = input.files?.[0];
|
|
174
|
+
if (!file)
|
|
175
|
+
return;
|
|
176
|
+
const url = onUploadImage ? await onUploadImage(file) : URL.createObjectURL(file);
|
|
177
|
+
editor.chain().focus().setImage({ src: url, alt: file.name }).run();
|
|
178
|
+
};
|
|
179
|
+
input.click();
|
|
180
|
+
}, [editor, onUploadImage]);
|
|
181
|
+
const Toolbar = React.useMemo(() => {
|
|
182
|
+
if (!toolbar)
|
|
183
|
+
return null;
|
|
184
|
+
const e = editor;
|
|
185
|
+
const run = (cb) => () => e && cb(e);
|
|
186
|
+
return (_jsxs("div", { className: "flex flex-wrap items-center gap-1 rounded-md border bg-muted/40 p-1", children: [allowedActions.includes("bold") && (_jsx(ToolbarButton, { label: "Negrito", active: e?.isActive("bold"), onClick: run((ed) => ed.chain().focus().toggleBold().run()), children: _jsx(Bold, { size: 14 }) })), allowedActions.includes("italic") && (_jsx(ToolbarButton, { label: "It\u00E1lico", active: e?.isActive("italic"), onClick: run((ed) => ed.chain().focus().toggleItalic().run()), children: _jsx(Italic, { size: 14 }) })), allowedActions.includes("strike") && (_jsx(ToolbarButton, { label: "Tachado", active: e?.isActive("strike"), onClick: run((ed) => ed.chain().focus().toggleStrike().run()), children: _jsx(Strikethrough, { size: 14 }) })), allowedActions.includes("code") && (_jsx(ToolbarButton, { label: "C\u00F3digo inline", active: e?.isActive("code"), onClick: run((ed) => ed.chain().focus().toggleCode().run()), children: _jsx(Code, { size: 14 }) })), _jsx(Separator, { orientation: "vertical", className: "mx-1 h-6" }), allowedActions.includes("h1") && (_jsx(ToolbarButton, { label: "T\u00EDtulo H1", active: e?.isActive("heading", { level: 1 }), onClick: run((ed) => ed.chain().focus().toggleHeading({ level: 1 }).run()), children: _jsx(Heading1, { size: 14 }) })), allowedActions.includes("h2") && (_jsx(ToolbarButton, { label: "T\u00EDtulo H2", active: e?.isActive("heading", { level: 2 }), onClick: run((ed) => ed.chain().focus().toggleHeading({ level: 2 }).run()), children: _jsx(Heading2, { size: 14 }) })), allowedActions.includes("h3") && (_jsx(ToolbarButton, { label: "T\u00EDtulo H3", active: e?.isActive("heading", { level: 3 }), onClick: run((ed) => ed.chain().focus().toggleHeading({ level: 3 }).run()), children: _jsx(Heading3, { size: 14 }) })), _jsx(Separator, { orientation: "vertical", className: "mx-1 h-6" }), allowedActions.includes("bulletList") && (_jsx(ToolbarButton, { label: "Lista n\u00E3o ordenada", active: e?.isActive("bulletList"), onClick: run((ed) => ed.chain().focus().toggleBulletList().run()), children: _jsx(ListIcon, { size: 14 }) })), allowedActions.includes("orderedList") && (_jsx(ToolbarButton, { label: "Lista ordenada", active: e?.isActive("orderedList"), onClick: run((ed) => ed.chain().focus().toggleOrderedList().run()), children: _jsx(ListOrdered, { size: 14 }) })), allowedActions.includes("blockquote") && (_jsx(ToolbarButton, { label: "Cita\u00E7\u00E3o", active: e?.isActive("blockquote"), onClick: run((ed) => ed.chain().focus().toggleBlockquote().run()), children: _jsx(Quote, { size: 14 }) })), _jsx(Separator, { orientation: "vertical", className: "mx-1 h-6" }), allowedActions.includes("alignLeft") && (_jsx(ToolbarButton, { label: "Alinhar \u00E0 esquerda", active: e?.isActive({ textAlign: "left" }), onClick: run((ed) => ed.chain().focus().setTextAlign("left").run()), children: _jsx(AlignLeft, { size: 14 }) })), allowedActions.includes("alignCenter") && (_jsx(ToolbarButton, { label: "Centralizar", active: e?.isActive({ textAlign: "center" }), onClick: run((ed) => ed.chain().focus().setTextAlign("center").run()), children: _jsx(AlignCenter, { size: 14 }) })), allowedActions.includes("alignRight") && (_jsx(ToolbarButton, { label: "Alinhar \u00E0 direita", active: e?.isActive({ textAlign: "right" }), onClick: run((ed) => ed.chain().focus().setTextAlign("right").run()), children: _jsx(AlignRight, { size: 14 }) })), _jsx(Separator, { orientation: "vertical", className: "mx-1 h-6" }), allowedActions.includes("textColor") && (_jsxs(Popover, { children: [_jsx(TooltipProvider, { delayDuration: 300, skipDelayDuration: 0, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-7 w-7 p-0", children: _jsx(Palette, { size: 14 }) }) }) }), _jsx(TooltipContent, { sideOffset: 4, children: "Cor do texto" })] }) }), _jsx(PopoverContent, { className: "w-auto p-2", children: _jsxs("div", { className: "grid grid-cols-6 gap-1", children: [PRESET_COLORS.map((color) => (_jsx("button", { type: "button", onClick: () => e?.chain().focus().setColor(color.value).run(), className: "size-6 rounded border border-border hover:scale-110 transition-transform", style: { backgroundColor: color.value }, title: color.label }, color.value))), _jsx("button", { type: "button", onClick: () => e?.chain().focus().unsetColor().run(), className: "size-6 rounded border border-border hover:scale-110 transition-transform bg-background flex items-center justify-center text-xs", title: "Remover cor", children: "\u2715" })] }) })] })), _jsx(Separator, { orientation: "vertical", className: "mx-1 h-6" }), allowedActions.includes("image") && (_jsx(ToolbarButton, { label: "Inserir imagem", onClick: handleImageInsert, children: _jsx(ImageIcon, { size: 14 }) }))] }));
|
|
187
|
+
}, [editor, toolbar, allowedActions, handleImageInsert]);
|
|
188
|
+
const richView = (_jsxs("div", { className: "grid gap-2", children: [Toolbar, _jsx("div", { className: cn("prose dark:prose-invert prose-sm max-w-none rounded-md border bg-background p-3", "focus-within:ring-ring/50 focus-within:ring-[3px]"), children: _jsx(EditorContent, { editor: editor, className: "min-h-40" }) })] }));
|
|
189
|
+
const markdownEditor = (_jsx(Textarea, { className: "font-mono text-sm", rows: 14, value: internalMarkdown, onChange: (e) => {
|
|
190
|
+
const next = e.target.value;
|
|
191
|
+
setInternalMarkdown(next);
|
|
192
|
+
if (outputFormat === "text") {
|
|
193
|
+
onChange?.(stripMarkdown(next));
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
onChange?.(next);
|
|
197
|
+
}
|
|
198
|
+
}, placeholder: placeholder }));
|
|
199
|
+
const previewView = (_jsx("div", { className: "rounded-md border bg-background p-3", children: _jsx(Streamdown, { children: internalMarkdown }) }));
|
|
200
|
+
// Renderização sem abas (apenas rich sozinho)
|
|
201
|
+
if (effectiveModes.length === 1 && effectiveModes[0] === "rich") {
|
|
202
|
+
return (_jsx("div", { ref: ref, className: cn("grid gap-2", className), ...props, children: richView }));
|
|
203
|
+
}
|
|
204
|
+
// Com abas (markdown sempre inclui preview automaticamente)
|
|
205
|
+
return (_jsx("div", { ref: ref, className: cn("grid gap-2", className), ...props, children: _jsxs(Tabs, { value: mode, onValueChange: (v) => setModeSafe(v), children: [_jsxs(TabsList, { className: "w-fit", children: [effectiveModes.includes("rich") && (_jsx(TabsTrigger, { value: "rich", children: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(Eye, { className: "size-3.5" }), " Rich"] }) })), effectiveModes.includes("markdown") && (_jsx(TabsTrigger, { value: "markdown", children: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(FileText, { className: "size-3.5" }), " Markdown"] }) })), effectiveModes.includes("preview") && (_jsx(TabsTrigger, { value: "preview", children: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(Eye, { className: "size-3.5" }), " Preview"] }) }))] }), effectiveModes.includes("rich") && (_jsx(TabsContent, { value: "rich", className: "mt-2", children: richView })), effectiveModes.includes("markdown") && (_jsx(TabsContent, { value: "markdown", className: "mt-2", children: markdownEditor })), effectiveModes.includes("preview") && (_jsx(TabsContent, { value: "preview", className: "mt-2", children: previewView }))] }) }));
|
|
206
|
+
});
|
|
207
|
+
TextEditor.displayName = "TextEditor";
|
|
208
|
+
function ToolbarButton({ label, active, className, children, ...rest }) {
|
|
209
|
+
return (_jsx(TooltipProvider, { delayDuration: 300, skipDelayDuration: 0, children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: cn("h-7 w-7 p-0", active && "bg-accent text-accent-foreground", className), ...rest, children: children }) }), _jsx(TooltipContent, { sideOffset: 4, children: label })] }) }));
|
|
210
|
+
}
|
|
211
|
+
export { DEFAULT_ACTIONS };
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
export type { ConnectionLineComponent } from "@xyflow/react";
|
|
6
6
|
export type { ModalBodyProps, ModalContentProps, ModalFooterProps, ModalProps, ModalTriggerProps, } from "./animation/animated-modal";
|
|
7
7
|
export { Modal, ModalBody, ModalContent, ModalFooter, ModalTrigger, } from "./animation/animated-modal";
|
|
8
|
+
export type { TextEditorMode, TextEditorProps, TextEditorToolbarAction } from "./forms/text-editor";
|
|
9
|
+
export { TextEditor } from "./forms/text-editor";
|
|
8
10
|
export type { AnimatedSlidingNumberProps } from "./animation/animated-sliding-number";
|
|
9
11
|
export { AnimatedSlidingNumber } from "./animation/animated-sliding-number";
|
|
10
12
|
export type { ButtonCounterProps } from "./animation/button-counter";
|