@exsto/ds 0.0.1-rc.13 → 0.0.1-rc.14
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/date-picker.js +1 -344
- package/dist/index.js +75 -4513
- package/dist/rich-text-editor.js +3 -904
- package/dist/source-hash.txt +1 -1
- package/dist/styles.css +5 -19
- package/dist/theme.css +2 -1
- package/dist/ui/src/components/edyn/animated-number/animated-number.js +108 -0
- package/dist/ui/src/components/edyn/animated-number/animated-number.js.map +1 -0
- package/dist/ui/src/components/edyn/audio-player/audio-player.js +86 -0
- package/dist/ui/src/components/edyn/audio-player/audio-player.js.map +1 -0
- package/dist/ui/src/components/edyn/date-picker/date-picker.js +250 -0
- package/dist/ui/src/components/edyn/date-picker/date-picker.js.map +1 -0
- package/dist/ui/src/components/edyn/date-picker/date-text.js +105 -0
- package/dist/ui/src/components/edyn/date-picker/date-text.js.map +1 -0
- package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.js +59 -0
- package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.js.map +1 -0
- package/dist/ui/src/components/edyn/initials-avatar/initials-avatar.js +86 -0
- package/dist/ui/src/components/edyn/initials-avatar/initials-avatar.js.map +1 -0
- package/dist/ui/src/components/edyn/multi-select/multi-select.js +332 -0
- package/dist/ui/src/components/edyn/multi-select/multi-select.js.map +1 -0
- package/dist/ui/src/components/edyn/progress-line/progress-line.js +100 -0
- package/dist/ui/src/components/edyn/progress-line/progress-line.js.map +1 -0
- package/dist/ui/src/components/edyn/rich-text-content/allow-list.js +37 -0
- package/dist/ui/src/components/edyn/rich-text-content/allow-list.js.map +1 -0
- package/dist/ui/src/components/edyn/rich-text-content/rich-text-content.js +124 -0
- package/dist/ui/src/components/edyn/rich-text-content/rich-text-content.js.map +1 -0
- package/dist/ui/src/components/edyn/rich-text-editor/extensions.js +124 -0
- package/dist/ui/src/components/edyn/rich-text-editor/extensions.js.map +1 -0
- package/dist/ui/src/components/edyn/rich-text-editor/mention-list.js +232 -0
- package/dist/ui/src/components/edyn/rich-text-editor/mention-list.js.map +1 -0
- package/dist/ui/src/components/edyn/rich-text-editor/rich-text-editor.js +244 -0
- package/dist/ui/src/components/edyn/rich-text-editor/rich-text-editor.js.map +1 -0
- package/dist/ui/src/components/edyn/rich-text-editor/serialize.js +55 -0
- package/dist/ui/src/components/edyn/rich-text-editor/serialize.js.map +1 -0
- package/dist/ui/src/components/edyn/rich-text-editor/sync.js +34 -0
- package/dist/ui/src/components/edyn/rich-text-editor/sync.js.map +1 -0
- package/dist/ui/src/components/edyn/rich-text-editor/toolbar.js +235 -0
- package/dist/ui/src/components/edyn/rich-text-editor/toolbar.js.map +1 -0
- package/dist/ui/src/components/edyn/rich-text-editor/types.js +26 -0
- package/dist/ui/src/components/edyn/rich-text-editor/types.js.map +1 -0
- package/dist/ui/src/components/edyn/video-player/video-player.js +132 -0
- package/dist/ui/src/components/edyn/video-player/video-player.js.map +1 -0
- package/dist/ui/src/components/ui/accordion/accordion.js +83 -0
- package/dist/ui/src/components/ui/accordion/accordion.js.map +1 -0
- package/dist/ui/src/components/ui/alert/alert.js +47 -0
- package/dist/ui/src/components/ui/alert/alert.js.map +1 -0
- package/dist/ui/src/components/ui/alert-dialog/alert-dialog.js +95 -0
- package/dist/ui/src/components/ui/alert-dialog/alert-dialog.js.map +1 -0
- package/dist/ui/src/components/ui/aspect-ratio/aspect-ratio.js +15 -0
- package/dist/ui/src/components/ui/aspect-ratio/aspect-ratio.js.map +1 -0
- package/dist/ui/src/components/ui/attachment/attachment.js +105 -0
- package/dist/ui/src/components/ui/attachment/attachment.js.map +1 -0
- package/dist/ui/src/components/ui/avatar/avatar.js +51 -0
- package/dist/ui/src/components/ui/avatar/avatar.js.map +1 -0
- package/dist/ui/src/components/ui/badge/badge.js +47 -0
- package/dist/ui/src/components/ui/badge/badge.js.map +1 -0
- package/dist/ui/src/components/ui/breadcrumb/breadcrumb.js +73 -0
- package/dist/ui/src/components/ui/breadcrumb/breadcrumb.js.map +1 -0
- package/dist/ui/src/components/ui/bubble/bubble.js +74 -0
- package/dist/ui/src/components/ui/bubble/bubble.js.map +1 -0
- package/dist/{chunks/input-syWxjY2U.js → ui/src/components/ui/button/button.js} +3 -82
- package/dist/ui/src/components/ui/button/button.js.map +1 -0
- package/dist/ui/src/components/ui/button-group/button-group.js +37 -0
- package/dist/ui/src/components/ui/button-group/button-group.js.map +1 -0
- package/dist/{chunks/date-range-picker-CWVcHgy2.js → ui/src/components/ui/calendar/calendar.js} +6 -100
- package/dist/ui/src/components/ui/calendar/calendar.js.map +1 -0
- package/dist/ui/src/components/ui/card/card.js +57 -0
- package/dist/ui/src/components/ui/card/card.js.map +1 -0
- package/dist/ui/src/components/ui/carousel/carousel.js +136 -0
- package/dist/ui/src/components/ui/carousel/carousel.js.map +1 -0
- package/dist/ui/src/components/ui/chart/chart.js +168 -0
- package/dist/ui/src/components/ui/chart/chart.js.map +1 -0
- package/dist/ui/src/components/ui/checkbox/checkbox.js +21 -0
- package/dist/ui/src/components/ui/checkbox/checkbox.js.map +1 -0
- package/dist/ui/src/components/ui/collapsible/collapsible.js +26 -0
- package/dist/ui/src/components/ui/collapsible/collapsible.js.map +1 -0
- package/dist/ui/src/components/ui/combobox/combobox.js +85 -0
- package/dist/ui/src/components/ui/combobox/combobox.js.map +1 -0
- package/dist/ui/src/components/ui/command/command.js +118 -0
- package/dist/ui/src/components/ui/command/command.js.map +1 -0
- package/dist/ui/src/components/ui/context-menu/context-menu.js +134 -0
- package/dist/ui/src/components/ui/context-menu/context-menu.js.map +1 -0
- package/dist/ui/src/components/ui/dialog/dialog.js +92 -0
- package/dist/ui/src/components/ui/dialog/dialog.js.map +1 -0
- package/dist/ui/src/components/ui/direction/direction.js +2 -0
- package/dist/ui/src/components/ui/drawer/drawer.js +86 -0
- package/dist/ui/src/components/ui/drawer/drawer.js.map +1 -0
- package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.js +160 -0
- package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.js.map +1 -0
- package/dist/ui/src/components/ui/empty/empty.js +61 -0
- package/dist/ui/src/components/ui/empty/empty.js.map +1 -0
- package/dist/ui/src/components/ui/field/field.js +36 -0
- package/dist/ui/src/components/ui/field/field.js.map +1 -0
- package/dist/ui/src/components/ui/hover-card/hover-card.js +38 -0
- package/dist/ui/src/components/ui/hover-card/hover-card.js.map +1 -0
- package/dist/ui/src/components/ui/indicator/indicator.js +79 -0
- package/dist/ui/src/components/ui/indicator/indicator.js.map +1 -0
- package/dist/ui/src/components/ui/input/input.js +16 -0
- package/dist/ui/src/components/ui/input/input.js.map +1 -0
- package/dist/ui/src/components/ui/input-group/input-group.js +77 -0
- package/dist/ui/src/components/ui/input-group/input-group.js.map +1 -0
- package/dist/ui/src/components/ui/input-otp/input-otp.js +48 -0
- package/dist/ui/src/components/ui/input-otp/input-otp.js.map +1 -0
- package/dist/ui/src/components/ui/item/item.js +122 -0
- package/dist/ui/src/components/ui/item/item.js.map +1 -0
- package/dist/ui/src/components/ui/kbd/kbd.js +21 -0
- package/dist/ui/src/components/ui/kbd/kbd.js.map +1 -0
- package/dist/ui/src/components/ui/label/label.js +14 -0
- package/dist/ui/src/components/ui/label/label.js.map +1 -0
- package/dist/ui/src/components/ui/marker/marker.js +44 -0
- package/dist/ui/src/components/ui/marker/marker.js.map +1 -0
- package/dist/ui/src/components/ui/menubar/menubar.js +137 -0
- package/dist/ui/src/components/ui/menubar/menubar.js.map +1 -0
- package/dist/ui/src/components/ui/message/message.js +50 -0
- package/dist/ui/src/components/ui/message/message.js.map +1 -0
- package/dist/ui/src/components/ui/message-scroller/message-scroller.js +61 -0
- package/dist/ui/src/components/ui/message-scroller/message-scroller.js.map +1 -0
- package/dist/ui/src/components/ui/native-select/native-select.js +39 -0
- package/dist/ui/src/components/ui/native-select/native-select.js.map +1 -0
- package/dist/ui/src/components/ui/navigation-menu/navigation-menu.js +84 -0
- package/dist/ui/src/components/ui/navigation-menu/navigation-menu.js.map +1 -0
- package/dist/ui/src/components/ui/pagination/pagination.js +104 -0
- package/dist/ui/src/components/ui/pagination/pagination.js.map +1 -0
- package/dist/ui/src/components/ui/popover/popover.js +56 -0
- package/dist/ui/src/components/ui/popover/popover.js.map +1 -0
- package/dist/ui/src/components/ui/progress/progress.js +70 -0
- package/dist/ui/src/components/ui/progress/progress.js.map +1 -0
- package/dist/ui/src/components/ui/questionnaire/questionnaire.js +171 -0
- package/dist/ui/src/components/ui/questionnaire/questionnaire.js.map +1 -0
- package/dist/ui/src/components/ui/radio-group/radio-group.js +29 -0
- package/dist/ui/src/components/ui/radio-group/radio-group.js.map +1 -0
- package/dist/ui/src/components/ui/resizable/resizable.js +29 -0
- package/dist/ui/src/components/ui/resizable/resizable.js.map +1 -0
- package/dist/ui/src/components/ui/scroll-area/scroll-area.js +119 -0
- package/dist/ui/src/components/ui/scroll-area/scroll-area.js.map +1 -0
- package/dist/ui/src/components/ui/select/select.js +99 -0
- package/dist/ui/src/components/ui/select/select.js.map +1 -0
- package/dist/ui/src/components/ui/separator/separator.js +30 -0
- package/dist/ui/src/components/ui/separator/separator.js.map +1 -0
- package/dist/ui/src/components/ui/sheet/sheet.js +89 -0
- package/dist/ui/src/components/ui/sheet/sheet.js.map +1 -0
- package/dist/ui/src/components/ui/sidebar/sidebar.js +383 -0
- package/dist/ui/src/components/ui/sidebar/sidebar.js.map +1 -0
- package/dist/ui/src/components/ui/skeleton/skeleton.js +14 -0
- package/dist/ui/src/components/ui/skeleton/skeleton.js.map +1 -0
- package/dist/ui/src/components/ui/slider/slider.js +49 -0
- package/dist/ui/src/components/ui/slider/slider.js.map +1 -0
- package/dist/ui/src/components/ui/spinner/spinner.js +28 -0
- package/dist/ui/src/components/ui/spinner/spinner.js.map +1 -0
- package/dist/ui/src/components/ui/switch/switch.js +20 -0
- package/dist/ui/src/components/ui/switch/switch.js.map +1 -0
- package/dist/ui/src/components/ui/table/table.js +68 -0
- package/dist/ui/src/components/ui/table/table.js.map +1 -0
- package/dist/ui/src/components/ui/tabs/tabs.js +50 -0
- package/dist/ui/src/components/ui/tabs/tabs.js.map +1 -0
- package/dist/ui/src/components/ui/textarea/textarea.js +14 -0
- package/dist/ui/src/components/ui/textarea/textarea.js.map +1 -0
- package/dist/ui/src/components/ui/toast/toast.js +124 -0
- package/dist/ui/src/components/ui/toast/toast.js.map +1 -0
- package/dist/ui/src/components/ui/toggle/toggle.js +37 -0
- package/dist/ui/src/components/ui/toggle/toggle.js.map +1 -0
- package/dist/ui/src/components/ui/toggle-group/toggle-group.js +56 -0
- package/dist/ui/src/components/ui/toggle-group/toggle-group.js.map +1 -0
- package/dist/ui/src/components/ui/tooltip/tooltip.js +42 -0
- package/dist/ui/src/components/ui/tooltip/tooltip.js.map +1 -0
- package/dist/ui/src/hooks/use-media-player.js +140 -0
- package/dist/ui/src/hooks/use-media-player.js.map +1 -0
- package/dist/ui/src/hooks/use-merged-refs.js +18 -0
- package/dist/ui/src/hooks/use-merged-refs.js.map +1 -0
- package/dist/ui/src/hooks/use-mobile.js +20 -0
- package/dist/ui/src/hooks/use-mobile.js.map +1 -0
- package/dist/ui/src/lib/first-weekday.js +34 -0
- package/dist/ui/src/lib/first-weekday.js.map +1 -0
- package/dist/ui/src/lib/popup-list.js +26 -0
- package/dist/ui/src/lib/popup-list.js.map +1 -0
- package/dist/ui/src/lib/utils.js +21 -0
- package/dist/ui/src/lib/utils.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunks/date-range-picker-CWVcHgy2.js.map +0 -1
- package/dist/chunks/input-syWxjY2U.js.map +0 -1
- package/dist/chunks/toggle-Bxg8P-61.js +0 -490
- package/dist/chunks/toggle-Bxg8P-61.js.map +0 -1
- package/dist/date-picker.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/rich-text-editor.js.map +0 -1
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { Button } from "../../ui/button/button.js";
|
|
3
|
+
import { Popover, PopoverContent, PopoverTrigger } from "../../ui/popover/popover.js";
|
|
4
|
+
import { isAllowedRichTextHref } from "../rich-text-content/allow-list.js";
|
|
5
|
+
import { Input } from "../../ui/input/input.js";
|
|
6
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../../ui/dropdown-menu/dropdown-menu.js";
|
|
7
|
+
import { toggleVariants } from "../../ui/toggle/toggle.js";
|
|
8
|
+
import { useEffect, useState } from "react";
|
|
9
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
import { IconBold, IconItalic, IconLink, IconList, IconListNumbers, IconSeparatorHorizontal, IconUnderline, IconVariable } from "@tabler/icons-react";
|
|
11
|
+
import { useEditorState } from "@tiptap/react";
|
|
12
|
+
import { Toolbar } from "@base-ui/react/toolbar";
|
|
13
|
+
//#region ../ui/src/components/edyn/rich-text-editor/toolbar.tsx
|
|
14
|
+
var BUTTON_CLASS = cn(toggleVariants({
|
|
15
|
+
variant: "default",
|
|
16
|
+
size: "sm"
|
|
17
|
+
}), "size-7 px-0");
|
|
18
|
+
/** A typed address becomes a link: "name@example.test" a mailto, "example.test" an https address. */
|
|
19
|
+
var normalizeHref = (raw) => {
|
|
20
|
+
const value = raw.trim();
|
|
21
|
+
if (value === "" || /^[a-z][a-z0-9+.-]*:/i.test(value)) return value;
|
|
22
|
+
return /^[^\s@/]+@[^\s@/]+\.[^\s@/]+$/.test(value) ? `mailto:${value}` : `https://${value}`;
|
|
23
|
+
};
|
|
24
|
+
var ToolbarButton = ({ label, pressed, disabled, onClick, children }) => /* @__PURE__ */ jsx(Toolbar.Button, {
|
|
25
|
+
type: "button",
|
|
26
|
+
"aria-label": label,
|
|
27
|
+
title: label,
|
|
28
|
+
"aria-pressed": pressed,
|
|
29
|
+
"data-pressed": pressed ? "" : void 0,
|
|
30
|
+
disabled,
|
|
31
|
+
className: BUTTON_CLASS,
|
|
32
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
33
|
+
onClick,
|
|
34
|
+
children
|
|
35
|
+
});
|
|
36
|
+
var hrefAt = (editor, range) => {
|
|
37
|
+
const { doc } = editor.state;
|
|
38
|
+
const marks = range.from < range.to ? doc.nodeAt(range.from)?.marks ?? [] : doc.resolve(range.from).marks();
|
|
39
|
+
return String(marks.find((mark) => mark.type.name === "link")?.attrs.href ?? "");
|
|
40
|
+
};
|
|
41
|
+
var LinkControl = ({ range, editor, labels, active, disabled, open, onOpenChange }) => {
|
|
42
|
+
const [address, setAddress] = useState("");
|
|
43
|
+
const [invalid, setInvalid] = useState(false);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (open) {
|
|
46
|
+
setAddress(range.current ? hrefAt(editor, range.current) : "");
|
|
47
|
+
setInvalid(false);
|
|
48
|
+
}
|
|
49
|
+
}, [
|
|
50
|
+
editor,
|
|
51
|
+
open,
|
|
52
|
+
range
|
|
53
|
+
]);
|
|
54
|
+
const apply = (event) => {
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
event.stopPropagation();
|
|
57
|
+
const href = normalizeHref(address);
|
|
58
|
+
if (!isAllowedRichTextHref(href)) {
|
|
59
|
+
setInvalid(true);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const saved = range.current ?? {
|
|
63
|
+
from: editor.state.selection.from,
|
|
64
|
+
to: editor.state.selection.to
|
|
65
|
+
};
|
|
66
|
+
const chain = editor.chain().focus().setTextSelection(saved);
|
|
67
|
+
if (saved.from === saved.to && !hrefAt(editor, saved)) chain.insertContent({
|
|
68
|
+
type: "text",
|
|
69
|
+
text: href,
|
|
70
|
+
marks: [{
|
|
71
|
+
type: "link",
|
|
72
|
+
attrs: { href }
|
|
73
|
+
}]
|
|
74
|
+
}).run();
|
|
75
|
+
else chain.extendMarkRange("link").setLink({ href }).run();
|
|
76
|
+
onOpenChange(false);
|
|
77
|
+
};
|
|
78
|
+
const remove = () => {
|
|
79
|
+
const saved = range.current ?? {
|
|
80
|
+
from: editor.state.selection.from,
|
|
81
|
+
to: editor.state.selection.to
|
|
82
|
+
};
|
|
83
|
+
editor.chain().focus().setTextSelection(saved).extendMarkRange("link").unsetLink().run();
|
|
84
|
+
onOpenChange(false);
|
|
85
|
+
};
|
|
86
|
+
return /* @__PURE__ */ jsxs(Popover, {
|
|
87
|
+
open,
|
|
88
|
+
onOpenChange,
|
|
89
|
+
children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
90
|
+
render: /* @__PURE__ */ jsx(Toolbar.Button, {
|
|
91
|
+
type: "button",
|
|
92
|
+
"aria-label": labels.link,
|
|
93
|
+
title: labels.link,
|
|
94
|
+
"aria-pressed": active,
|
|
95
|
+
"data-pressed": active ? "" : void 0,
|
|
96
|
+
disabled,
|
|
97
|
+
className: BUTTON_CLASS
|
|
98
|
+
}),
|
|
99
|
+
children: /* @__PURE__ */ jsx(IconLink, { "aria-hidden": true })
|
|
100
|
+
}), /* @__PURE__ */ jsx(PopoverContent, {
|
|
101
|
+
align: "start",
|
|
102
|
+
className: "w-80",
|
|
103
|
+
children: /* @__PURE__ */ jsxs("form", {
|
|
104
|
+
className: "flex flex-col gap-2",
|
|
105
|
+
onSubmit: apply,
|
|
106
|
+
children: [
|
|
107
|
+
/* @__PURE__ */ jsx(Input, {
|
|
108
|
+
type: "text",
|
|
109
|
+
inputMode: "url",
|
|
110
|
+
"aria-label": labels.linkAddress,
|
|
111
|
+
"aria-invalid": invalid || void 0,
|
|
112
|
+
value: address,
|
|
113
|
+
onChange: (event) => {
|
|
114
|
+
setAddress(event.target.value);
|
|
115
|
+
setInvalid(false);
|
|
116
|
+
}
|
|
117
|
+
}),
|
|
118
|
+
invalid ? /* @__PURE__ */ jsx("p", {
|
|
119
|
+
className: "text-xs text-destructive",
|
|
120
|
+
children: labels.linkInvalid
|
|
121
|
+
}) : null,
|
|
122
|
+
/* @__PURE__ */ jsxs("div", {
|
|
123
|
+
className: "flex justify-end gap-2",
|
|
124
|
+
children: [active ? /* @__PURE__ */ jsx(Button, {
|
|
125
|
+
type: "button",
|
|
126
|
+
variant: "outline",
|
|
127
|
+
size: "sm",
|
|
128
|
+
onClick: remove,
|
|
129
|
+
children: labels.linkRemove
|
|
130
|
+
}) : null, /* @__PURE__ */ jsx(Button, {
|
|
131
|
+
type: "submit",
|
|
132
|
+
size: "sm",
|
|
133
|
+
children: labels.linkApply
|
|
134
|
+
})]
|
|
135
|
+
})
|
|
136
|
+
]
|
|
137
|
+
})
|
|
138
|
+
})]
|
|
139
|
+
});
|
|
140
|
+
};
|
|
141
|
+
var RichTextToolbar = ({ linkRange, editor, labels, fields, disabled, linkOpen, onLinkOpenChange }) => {
|
|
142
|
+
const state = useEditorState({
|
|
143
|
+
editor,
|
|
144
|
+
selector: ({ editor: current }) => ({
|
|
145
|
+
bold: current.isActive("bold"),
|
|
146
|
+
italic: current.isActive("italic"),
|
|
147
|
+
underline: current.isActive("underline"),
|
|
148
|
+
bullets: current.isActive("bulletList"),
|
|
149
|
+
numbers: current.isActive("orderedList"),
|
|
150
|
+
link: current.isActive("link")
|
|
151
|
+
})
|
|
152
|
+
});
|
|
153
|
+
return /* @__PURE__ */ jsxs(Toolbar.Root, {
|
|
154
|
+
"data-slot": "rich-text-toolbar",
|
|
155
|
+
"aria-label": labels.toolbar,
|
|
156
|
+
className: "flex flex-wrap items-center gap-0.5 border-b border-input p-1",
|
|
157
|
+
children: [
|
|
158
|
+
/* @__PURE__ */ jsx(ToolbarButton, {
|
|
159
|
+
label: labels.bold,
|
|
160
|
+
pressed: state.bold,
|
|
161
|
+
disabled,
|
|
162
|
+
onClick: () => editor.chain().focus().toggleBold().run(),
|
|
163
|
+
children: /* @__PURE__ */ jsx(IconBold, { "aria-hidden": true })
|
|
164
|
+
}),
|
|
165
|
+
/* @__PURE__ */ jsx(ToolbarButton, {
|
|
166
|
+
label: labels.italic,
|
|
167
|
+
pressed: state.italic,
|
|
168
|
+
disabled,
|
|
169
|
+
onClick: () => editor.chain().focus().toggleItalic().run(),
|
|
170
|
+
children: /* @__PURE__ */ jsx(IconItalic, { "aria-hidden": true })
|
|
171
|
+
}),
|
|
172
|
+
/* @__PURE__ */ jsx(ToolbarButton, {
|
|
173
|
+
label: labels.underline,
|
|
174
|
+
pressed: state.underline,
|
|
175
|
+
disabled,
|
|
176
|
+
onClick: () => editor.chain().focus().toggleUnderline().run(),
|
|
177
|
+
children: /* @__PURE__ */ jsx(IconUnderline, { "aria-hidden": true })
|
|
178
|
+
}),
|
|
179
|
+
/* @__PURE__ */ jsx(Toolbar.Separator, { className: "mx-1 h-4 w-px bg-border" }),
|
|
180
|
+
/* @__PURE__ */ jsx(ToolbarButton, {
|
|
181
|
+
label: labels.bulletList,
|
|
182
|
+
pressed: state.bullets,
|
|
183
|
+
disabled,
|
|
184
|
+
onClick: () => editor.chain().focus().toggleBulletList().run(),
|
|
185
|
+
children: /* @__PURE__ */ jsx(IconList, { "aria-hidden": true })
|
|
186
|
+
}),
|
|
187
|
+
/* @__PURE__ */ jsx(ToolbarButton, {
|
|
188
|
+
label: labels.numberedList,
|
|
189
|
+
pressed: state.numbers,
|
|
190
|
+
disabled,
|
|
191
|
+
onClick: () => editor.chain().focus().toggleOrderedList().run(),
|
|
192
|
+
children: /* @__PURE__ */ jsx(IconListNumbers, { "aria-hidden": true })
|
|
193
|
+
}),
|
|
194
|
+
/* @__PURE__ */ jsx(LinkControl, {
|
|
195
|
+
range: linkRange,
|
|
196
|
+
editor,
|
|
197
|
+
labels,
|
|
198
|
+
active: state.link,
|
|
199
|
+
disabled,
|
|
200
|
+
open: linkOpen,
|
|
201
|
+
onOpenChange: onLinkOpenChange
|
|
202
|
+
}),
|
|
203
|
+
/* @__PURE__ */ jsx(ToolbarButton, {
|
|
204
|
+
label: labels.horizontalRule,
|
|
205
|
+
disabled,
|
|
206
|
+
onClick: () => editor.chain().focus().setHorizontalRule().run(),
|
|
207
|
+
children: /* @__PURE__ */ jsx(IconSeparatorHorizontal, { "aria-hidden": true })
|
|
208
|
+
}),
|
|
209
|
+
fields && fields.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Toolbar.Separator, { className: "mx-1 h-4 w-px bg-border" }), /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
|
|
210
|
+
render: /* @__PURE__ */ jsx(Toolbar.Button, {
|
|
211
|
+
type: "button",
|
|
212
|
+
"aria-label": labels.insertField,
|
|
213
|
+
title: labels.insertField,
|
|
214
|
+
disabled,
|
|
215
|
+
className: BUTTON_CLASS
|
|
216
|
+
}),
|
|
217
|
+
children: /* @__PURE__ */ jsx(IconVariable, { "aria-hidden": true })
|
|
218
|
+
}), /* @__PURE__ */ jsx(DropdownMenuContent, {
|
|
219
|
+
align: "start",
|
|
220
|
+
className: "w-56",
|
|
221
|
+
children: fields.map((field) => /* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
222
|
+
onClick: () => editor.chain().focus().insertContent({
|
|
223
|
+
type: "text",
|
|
224
|
+
text: `{{${field.id}}}`
|
|
225
|
+
}).run(),
|
|
226
|
+
children: field.label
|
|
227
|
+
}, field.id))
|
|
228
|
+
})] })] }) : null
|
|
229
|
+
]
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
//#endregion
|
|
233
|
+
export { RichTextToolbar, normalizeHref };
|
|
234
|
+
|
|
235
|
+
//# sourceMappingURL=toolbar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolbar.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/rich-text-editor/toolbar.tsx"],"sourcesContent":["import { Toolbar as ToolbarPrimitive } from '@base-ui/react/toolbar';\nimport { isAllowedRichTextHref } from '@edyn/ui/components/edyn/rich-text-content/allow-list';\nimport { Button } from '@edyn/ui/components/ui/button';\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@edyn/ui/components/ui/dropdown-menu';\nimport { Input } from '@edyn/ui/components/ui/input';\nimport { Popover, PopoverContent, PopoverTrigger } from '@edyn/ui/components/ui/popover';\nimport { toggleVariants } from '@edyn/ui/components/ui/toggle';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconBold, IconItalic, IconLink, IconList, IconListNumbers, IconSeparatorHorizontal, IconUnderline, IconVariable } from '@tabler/icons-react';\nimport type { Editor } from '@tiptap/core';\nimport { useEditorState } from '@tiptap/react';\nimport { type FormEvent, type ReactNode, type RefObject, useEffect, useState } from 'react';\nimport type { IRichTextField, IRichTextLabels } from './types';\n\nconst BUTTON_CLASS = cn(toggleVariants({ variant: 'default', size: 'sm' }), 'size-7 px-0');\n\n/** A typed address becomes a link: \"name@example.test\" a mailto, \"example.test\" an https address. */\nexport const normalizeHref = (raw: string): string => {\n const value = raw.trim();\n if (value === '' || /^[a-z][a-z0-9+.-]*:/i.test(value)) {\n return value;\n }\n return /^[^\\s@/]+@[^\\s@/]+\\.[^\\s@/]+$/.test(value) ? `mailto:${value}` : `https://${value}`;\n};\n\ninterface IToolbarButtonProps {\n label: string;\n pressed?: boolean;\n disabled?: boolean;\n onClick?: () => void;\n children: ReactNode;\n}\n\nconst ToolbarButton = ({ label, pressed, disabled, onClick, children }: IToolbarButtonProps) => (\n <ToolbarPrimitive.Button\n type=\"button\"\n aria-label={label}\n title={label}\n aria-pressed={pressed}\n data-pressed={pressed ? '' : undefined}\n disabled={disabled}\n className={BUTTON_CLASS}\n onMouseDown={(event) => event.preventDefault()}\n onClick={onClick}\n >\n {children}\n </ToolbarPrimitive.Button>\n);\n\n/** Where the writer's selection was when the link box opened: moving focus into the box must not lose it. */\nexport interface ILinkRange {\n from: number;\n to: number;\n}\n\nconst hrefAt = (editor: Editor, range: ILinkRange): string => {\n const { doc } = editor.state;\n const marks = range.from < range.to ? (doc.nodeAt(range.from)?.marks ?? []) : doc.resolve(range.from).marks();\n return String(marks.find((mark) => mark.type.name === 'link')?.attrs.href ?? '');\n};\n\ninterface ILinkControlProps {\n range: RefObject<ILinkRange | null>;\n editor: Editor;\n labels: IRichTextLabels;\n active: boolean;\n disabled: boolean;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n}\n\nconst LinkControl = ({ range, editor, labels, active, disabled, open, onOpenChange }: ILinkControlProps) => {\n const [address, setAddress] = useState('');\n const [invalid, setInvalid] = useState(false);\n\n useEffect(() => {\n if (open) {\n setAddress(range.current ? hrefAt(editor, range.current) : '');\n setInvalid(false);\n }\n }, [editor, open, range]);\n\n const apply = (event: FormEvent) => {\n event.preventDefault();\n event.stopPropagation();\n const href = normalizeHref(address);\n if (!isAllowedRichTextHref(href)) {\n setInvalid(true);\n return;\n }\n const saved = range.current ?? { from: editor.state.selection.from, to: editor.state.selection.to };\n const chain = editor.chain().focus().setTextSelection(saved);\n if (saved.from === saved.to && !hrefAt(editor, saved)) {\n chain.insertContent({ type: 'text', text: href, marks: [{ type: 'link', attrs: { href } }] }).run();\n } else {\n chain.extendMarkRange('link').setLink({ href }).run();\n }\n onOpenChange(false);\n };\n\n const remove = () => {\n const saved = range.current ?? { from: editor.state.selection.from, to: editor.state.selection.to };\n editor.chain().focus().setTextSelection(saved).extendMarkRange('link').unsetLink().run();\n onOpenChange(false);\n };\n\n return (\n <Popover open={open} onOpenChange={onOpenChange}>\n <PopoverTrigger\n render={\n <ToolbarPrimitive.Button\n type=\"button\"\n aria-label={labels.link}\n title={labels.link}\n aria-pressed={active}\n data-pressed={active ? '' : undefined}\n disabled={disabled}\n className={BUTTON_CLASS}\n />\n }\n >\n <IconLink aria-hidden />\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-80\">\n <form className=\"flex flex-col gap-2\" onSubmit={apply}>\n <Input\n type=\"text\"\n inputMode=\"url\"\n aria-label={labels.linkAddress}\n aria-invalid={invalid || undefined}\n value={address}\n onChange={(event) => {\n setAddress(event.target.value);\n setInvalid(false);\n }}\n />\n {invalid ? <p className=\"text-xs text-destructive\">{labels.linkInvalid}</p> : null}\n <div className=\"flex justify-end gap-2\">\n {active ? (\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={remove}>\n {labels.linkRemove}\n </Button>\n ) : null}\n <Button type=\"submit\" size=\"sm\">\n {labels.linkApply}\n </Button>\n </div>\n </form>\n </PopoverContent>\n </Popover>\n );\n};\n\nexport interface IRichTextToolbarProps {\n linkRange: RefObject<ILinkRange | null>;\n editor: Editor;\n labels: IRichTextLabels;\n fields: IRichTextField[] | undefined;\n disabled: boolean;\n linkOpen: boolean;\n onLinkOpenChange: (open: boolean) => void;\n}\n\nexport const RichTextToolbar = ({ linkRange, editor, labels, fields, disabled, linkOpen, onLinkOpenChange }: IRichTextToolbarProps) => {\n const state = useEditorState({\n editor,\n selector: ({ editor: current }) => ({\n bold: current.isActive('bold'),\n italic: current.isActive('italic'),\n underline: current.isActive('underline'),\n bullets: current.isActive('bulletList'),\n numbers: current.isActive('orderedList'),\n link: current.isActive('link'),\n }),\n });\n\n return (\n <ToolbarPrimitive.Root\n data-slot=\"rich-text-toolbar\"\n aria-label={labels.toolbar}\n className=\"flex flex-wrap items-center gap-0.5 border-b border-input p-1\"\n >\n <ToolbarButton label={labels.bold} pressed={state.bold} disabled={disabled} onClick={() => editor.chain().focus().toggleBold().run()}>\n <IconBold aria-hidden />\n </ToolbarButton>\n <ToolbarButton label={labels.italic} pressed={state.italic} disabled={disabled} onClick={() => editor.chain().focus().toggleItalic().run()}>\n <IconItalic aria-hidden />\n </ToolbarButton>\n <ToolbarButton\n label={labels.underline}\n pressed={state.underline}\n disabled={disabled}\n onClick={() => editor.chain().focus().toggleUnderline().run()}\n >\n <IconUnderline aria-hidden />\n </ToolbarButton>\n <ToolbarPrimitive.Separator className=\"mx-1 h-4 w-px bg-border\" />\n <ToolbarButton\n label={labels.bulletList}\n pressed={state.bullets}\n disabled={disabled}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n >\n <IconList aria-hidden />\n </ToolbarButton>\n <ToolbarButton\n label={labels.numberedList}\n pressed={state.numbers}\n disabled={disabled}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n >\n <IconListNumbers aria-hidden />\n </ToolbarButton>\n <LinkControl\n range={linkRange}\n editor={editor}\n labels={labels}\n active={state.link}\n disabled={disabled}\n open={linkOpen}\n onOpenChange={onLinkOpenChange}\n />\n <ToolbarButton label={labels.horizontalRule} disabled={disabled} onClick={() => editor.chain().focus().setHorizontalRule().run()}>\n <IconSeparatorHorizontal aria-hidden />\n </ToolbarButton>\n {fields && fields.length > 0 ? (\n <>\n <ToolbarPrimitive.Separator className=\"mx-1 h-4 w-px bg-border\" />\n <DropdownMenu>\n <DropdownMenuTrigger\n render={\n <ToolbarPrimitive.Button\n type=\"button\"\n aria-label={labels.insertField}\n title={labels.insertField}\n disabled={disabled}\n className={BUTTON_CLASS}\n />\n }\n >\n <IconVariable aria-hidden />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"w-56\">\n {fields.map((field) => (\n <DropdownMenuItem\n key={field.id}\n onClick={() =>\n editor\n .chain()\n .focus()\n .insertContent({ type: 'text', text: `{{${field.id}}}` })\n .run()\n }\n >\n {field.label}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n </>\n ) : null}\n </ToolbarPrimitive.Root>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region ../ui/src/components/edyn/rich-text-editor/types.ts
|
|
2
|
+
var DEFAULT_RICH_TEXT_LABELS = {
|
|
3
|
+
toolbar: "Formatting",
|
|
4
|
+
bold: "Bold",
|
|
5
|
+
italic: "Italic",
|
|
6
|
+
underline: "Underline",
|
|
7
|
+
bulletList: "Bulleted list",
|
|
8
|
+
numberedList: "Numbered list",
|
|
9
|
+
link: "Link",
|
|
10
|
+
horizontalRule: "Horizontal line",
|
|
11
|
+
insertField: "Insert a field",
|
|
12
|
+
linkAddress: "Link address",
|
|
13
|
+
linkApply: "Apply",
|
|
14
|
+
linkRemove: "Remove link",
|
|
15
|
+
linkInvalid: "Enter an http, https or mailto address.",
|
|
16
|
+
mentionList: "Mentions",
|
|
17
|
+
mentionSearching: "Searching...",
|
|
18
|
+
mentionEmpty: "No matches",
|
|
19
|
+
mentionError: "The search failed",
|
|
20
|
+
mentionLoadingMore: "Loading more...",
|
|
21
|
+
mentionResults: "{count} results"
|
|
22
|
+
};
|
|
23
|
+
//#endregion
|
|
24
|
+
export { DEFAULT_RICH_TEXT_LABELS };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/rich-text-editor/types.ts"],"sourcesContent":["/** One person (or anything else) the app offers when the writer types @. */\nexport interface IRichTextMentionItem {\n /** What the HTML carries. By default the value is written as `@${id}`, so an email address works as an id. */\n id: string;\n /** What the chip shows while the text is being edited. */\n label: string;\n /** A second, muted line in the list (a role, an email address). */\n secondary?: string;\n}\n\n/** A page of results. Return a plain array instead when there is only ever one page. */\nexport interface IRichTextMentionPage {\n items: IRichTextMentionItem[];\n /** True when asking for `page + 1` would return more. The list loads it when it is scrolled to the end. */\n hasMore?: boolean;\n}\n\nexport interface IRichTextMentionSearchOptions {\n /** 1-based. */\n page: number;\n /** Aborted when the writer types on or closes the list: pass it to `fetch` so a stale search stops. */\n signal: AbortSignal;\n}\n\nexport type TRichTextMentionSearch = (\n query: string,\n options: IRichTextMentionSearchOptions,\n) => Promise<IRichTextMentionItem[] | IRichTextMentionPage>;\n\nexport interface IRichTextMention {\n /** Asked for the first page when the writer types @ and again for every change of the text after it, debounced. */\n search: TRichTextMentionSearch;\n /**\n * How a mention is written into the HTML. The chip exists only while editing; the HTML carries this text\n * and no element. Default: `@${item.id}`.\n */\n toText?: (item: Pick<IRichTextMentionItem, 'id' | 'label'>) => string;\n /** Milliseconds to wait after the last keystroke before searching. Default 250. */\n debounce?: number;\n}\n\n/** A field of a template (a mail merge token). Inserted as the plain text `{{id}}`. */\nexport interface IRichTextField {\n id: string;\n label: string;\n}\n\n/** What a ref to the editor can do, for a button outside it (a merge tag, say). */\nexport interface IRichTextEditorHandle {\n /** Writes plain text at the caret (over the selection), and puts focus back in the editor. */\n insertText: (text: string) => void;\n focus: () => void;\n}\n\n/** Every string the editor shows, so an app can translate them. */\nexport interface IRichTextLabels {\n toolbar: string;\n bold: string;\n italic: string;\n underline: string;\n bulletList: string;\n numberedList: string;\n link: string;\n horizontalRule: string;\n insertField: string;\n linkAddress: string;\n linkApply: string;\n linkRemove: string;\n linkInvalid: string;\n mentionList: string;\n mentionSearching: string;\n mentionEmpty: string;\n mentionError: string;\n mentionLoadingMore: string;\n /** Announced as the list changes. `{count}` is replaced. */\n mentionResults: string;\n}\n\nexport const DEFAULT_RICH_TEXT_LABELS: IRichTextLabels = {\n toolbar: 'Formatting',\n bold: 'Bold',\n italic: 'Italic',\n underline: 'Underline',\n bulletList: 'Bulleted list',\n numberedList: 'Numbered list',\n link: 'Link',\n horizontalRule: 'Horizontal line',\n insertField: 'Insert a field',\n linkAddress: 'Link address',\n linkApply: 'Apply',\n linkRemove: 'Remove link',\n linkInvalid: 'Enter an http, https or mailto address.',\n mentionList: 'Mentions',\n mentionSearching: 'Searching...',\n mentionEmpty: 'No matches',\n mentionError: 'The search failed',\n mentionLoadingMore: 'Loading more...',\n mentionResults: '{count} results',\n};\n"],"mappings":";AA8EA,IAAa,2BAA4C;CACvD,SAAS;CACT,MAAM;CACN,QAAQ;CACR,WAAW;CACX,YAAY;CACZ,cAAc;CACd,MAAM;CACN,gBAAgB;CAChB,aAAa;CACb,aAAa;CACb,WAAW;CACX,YAAY;CACZ,aAAa;CACb,aAAa;CACb,kBAAkB;CAClB,cAAc;CACd,cAAc;CACd,oBAAoB;CACpB,gBAAgB;AAClB"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { cn, cssLength } from "../../../lib/utils.js";
|
|
2
|
+
import { Button } from "../../ui/button/button.js";
|
|
3
|
+
import { Slider } from "../../ui/slider/slider.js";
|
|
4
|
+
import { formatMediaTime, useMediaPlayer } from "../../../hooks/use-media-player.js";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { IconMaximize, IconPlayerPauseFilled, IconPlayerPlayFilled, IconVolume, IconVolumeOff } from "@tabler/icons-react";
|
|
8
|
+
//#region ../ui/src/components/edyn/video-player/video-player.tsx
|
|
9
|
+
/**
|
|
10
|
+
* Media surface with an overlaid control bar: title and description, elapsed and
|
|
11
|
+
* total time, a seekable scrubber, play/pause, volume, and fullscreen.
|
|
12
|
+
*
|
|
13
|
+
* The root carries the `dark` class so its tokens resolve to the dark palette in
|
|
14
|
+
* both themes. Video is dark, so the letterbox and controls must stay dark and
|
|
15
|
+
* light-on-dark; this re-scopes existing tokens rather than hardcoding a colour.
|
|
16
|
+
*/
|
|
17
|
+
var VideoPlayer = ({ src, poster, title, description, muted = false, loop = false, disabled = false, width, height, ratio = "auto", className, "aria-label": ariaLabel = "Video player", style, onPlay, onPause, onEnded, onTimeUpdate, ...props }) => {
|
|
18
|
+
const { refMedia, playing, currentTime, duration, muted: isMuted, toggle, seek, toggleMute } = useMediaPlayer({
|
|
19
|
+
onPlay,
|
|
20
|
+
onPause,
|
|
21
|
+
onEnded,
|
|
22
|
+
onTimeUpdate
|
|
23
|
+
});
|
|
24
|
+
const refContainer = React.useRef(null);
|
|
25
|
+
const PlaybackIcon = playing ? IconPlayerPauseFilled : IconPlayerPlayFilled;
|
|
26
|
+
const VolumeIcon = isMuted ? IconVolumeOff : IconVolume;
|
|
27
|
+
const isSizedBox = height != null || ratio !== "auto";
|
|
28
|
+
const requestFullscreen = () => {
|
|
29
|
+
const container = refContainer.current;
|
|
30
|
+
if (!container) return;
|
|
31
|
+
if (document.fullscreenElement) {
|
|
32
|
+
document.exitFullscreen().catch(() => void 0);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
container.requestFullscreen?.().catch(() => void 0);
|
|
36
|
+
};
|
|
37
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
38
|
+
ref: refContainer,
|
|
39
|
+
"data-slot": "video-player",
|
|
40
|
+
...props,
|
|
41
|
+
"data-playing": playing || void 0,
|
|
42
|
+
style: {
|
|
43
|
+
width: cssLength(width),
|
|
44
|
+
height: cssLength(height),
|
|
45
|
+
aspectRatio: height == null && ratio !== "auto" ? ratio : void 0,
|
|
46
|
+
...style
|
|
47
|
+
},
|
|
48
|
+
className: cn("dark relative isolate w-full overflow-hidden rounded-lg bg-background", className),
|
|
49
|
+
children: [/* @__PURE__ */ jsx("video", {
|
|
50
|
+
ref: refMedia,
|
|
51
|
+
src,
|
|
52
|
+
poster,
|
|
53
|
+
muted,
|
|
54
|
+
loop,
|
|
55
|
+
playsInline: true,
|
|
56
|
+
preload: "metadata",
|
|
57
|
+
"aria-label": ariaLabel,
|
|
58
|
+
className: cn("w-full bg-background", isSizedBox ? "h-full object-contain" : "h-auto")
|
|
59
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
60
|
+
className: "pointer-events-none absolute inset-x-0 bottom-0 flex flex-col gap-0.5 bg-gradient-to-t from-background via-background/75 to-transparent px-3 pt-12 pb-2 text-foreground",
|
|
61
|
+
children: [
|
|
62
|
+
/* @__PURE__ */ jsxs("div", {
|
|
63
|
+
className: "pointer-events-auto flex items-end justify-between gap-4",
|
|
64
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
65
|
+
className: "min-w-0",
|
|
66
|
+
children: [title ? /* @__PURE__ */ jsx("p", {
|
|
67
|
+
className: "truncate text-sm font-medium text-foreground",
|
|
68
|
+
children: title
|
|
69
|
+
}) : null, description ? /* @__PURE__ */ jsx("p", {
|
|
70
|
+
className: "truncate text-xs text-foreground/70",
|
|
71
|
+
children: description
|
|
72
|
+
}) : null]
|
|
73
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
74
|
+
className: "shrink-0 text-xs tabular-nums text-foreground/70",
|
|
75
|
+
children: [
|
|
76
|
+
formatMediaTime(currentTime),
|
|
77
|
+
" / ",
|
|
78
|
+
formatMediaTime(duration)
|
|
79
|
+
]
|
|
80
|
+
})]
|
|
81
|
+
}),
|
|
82
|
+
/* @__PURE__ */ jsx(Slider, {
|
|
83
|
+
className: "pointer-events-auto [&_[data-slot=slider-control]]:py-1.5 [&_[data-slot=slider-thumb]]:size-3 [&_[data-slot=slider-track]]:h-1",
|
|
84
|
+
value: Math.min(currentTime, duration || 0),
|
|
85
|
+
min: 0,
|
|
86
|
+
max: duration || 0,
|
|
87
|
+
step: 1,
|
|
88
|
+
disabled: disabled || duration === 0,
|
|
89
|
+
"aria-label": "Seek",
|
|
90
|
+
onValueChange: (next) => {
|
|
91
|
+
seek(next);
|
|
92
|
+
}
|
|
93
|
+
}),
|
|
94
|
+
/* @__PURE__ */ jsxs("div", {
|
|
95
|
+
className: "pointer-events-auto flex items-center justify-between",
|
|
96
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
97
|
+
className: "flex items-center gap-1",
|
|
98
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
99
|
+
type: "button",
|
|
100
|
+
variant: "ghost",
|
|
101
|
+
size: "icon",
|
|
102
|
+
disabled,
|
|
103
|
+
"aria-label": playing ? "Pause" : "Play",
|
|
104
|
+
onClick: toggle,
|
|
105
|
+
children: /* @__PURE__ */ jsx(PlaybackIcon, {})
|
|
106
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
107
|
+
type: "button",
|
|
108
|
+
variant: "ghost",
|
|
109
|
+
size: "icon",
|
|
110
|
+
disabled,
|
|
111
|
+
"aria-label": isMuted ? "Unmute" : "Mute",
|
|
112
|
+
onClick: toggleMute,
|
|
113
|
+
children: /* @__PURE__ */ jsx(VolumeIcon, {})
|
|
114
|
+
})]
|
|
115
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
116
|
+
type: "button",
|
|
117
|
+
variant: "ghost",
|
|
118
|
+
size: "icon",
|
|
119
|
+
disabled,
|
|
120
|
+
"aria-label": "Fullscreen",
|
|
121
|
+
onClick: requestFullscreen,
|
|
122
|
+
children: /* @__PURE__ */ jsx(IconMaximize, {})
|
|
123
|
+
})]
|
|
124
|
+
})
|
|
125
|
+
]
|
|
126
|
+
})]
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
//#endregion
|
|
130
|
+
export { VideoPlayer };
|
|
131
|
+
|
|
132
|
+
//# sourceMappingURL=video-player.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"video-player.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/video-player/video-player.tsx"],"sourcesContent":["import { Button } from '@edyn/ui/components/ui/button';\nimport { Slider } from '@edyn/ui/components/ui/slider';\nimport { formatMediaTime, useMediaPlayer } from '@edyn/ui/hooks/use-media-player';\nimport type { TCssLength } from '@edyn/ui/lib/utils';\nimport { cn, cssLength } from '@edyn/ui/lib/utils';\nimport { IconMaximize, IconPlayerPauseFilled, IconPlayerPlayFilled, IconVolume, IconVolumeOff } from '@tabler/icons-react';\nimport * as React from 'react';\n\nexport interface IVideoPlayerProps\n extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children' | 'className' | 'title' | 'onPlay' | 'onPause' | 'onEnded' | 'onTimeUpdate'> {\n /** Video source URL. */\n src?: string;\n /** Still frame shown before playback starts. */\n poster?: string;\n /** Primary label rendered in the control overlay. */\n title?: string;\n /** Secondary label rendered under the title. */\n description?: string;\n /** Start muted. Required by browsers for unattended autoplay. */\n muted?: boolean;\n /** Loop playback when the video ends. */\n loop?: boolean;\n /** Disable every control without hiding the player. */\n disabled?: boolean;\n /**\n * Box width. A number is px, a string is any CSS length (`'100%'`, `'34rem'`).\n * Omitted, the player fills the width it is given.\n */\n width?: TCssLength;\n /**\n * Box height. A number is px, a string is any CSS length. Omitted, the height\n * comes from `ratio`. Setting this letterboxes a source that does not match.\n */\n height?: TCssLength;\n /**\n * Aspect ratio of the media box, as a CSS `aspect-ratio` value (`'16/9'`).\n *\n * Defaults to `auto`, so the height follows the source's own dimensions and a\n * portrait or 4:3 clip is shown whole rather than letterboxed. Pair `auto`\n * with a `poster`: a `<video>` reports a 300x150 intrinsic size until metadata\n * arrives, and the poster's dimensions stand in for it, which is what stops\n * the box from resizing when the first frame lands.\n *\n * Ignored when `height` is set.\n */\n ratio?: string;\n className?: string;\n 'aria-label'?: string;\n onPlay?: () => void;\n onPause?: () => void;\n onEnded?: () => void;\n onTimeUpdate?: (currentTime: number, duration: number) => void;\n}\n\n/**\n * Media surface with an overlaid control bar: title and description, elapsed and\n * total time, a seekable scrubber, play/pause, volume, and fullscreen.\n *\n * The root carries the `dark` class so its tokens resolve to the dark palette in\n * both themes. Video is dark, so the letterbox and controls must stay dark and\n * light-on-dark; this re-scopes existing tokens rather than hardcoding a colour.\n */\nexport const VideoPlayer = ({\n src,\n poster,\n title,\n description,\n muted = false,\n loop = false,\n disabled = false,\n width,\n height,\n ratio = 'auto',\n className,\n 'aria-label': ariaLabel = 'Video player',\n style,\n onPlay,\n onPause,\n onEnded,\n onTimeUpdate,\n ...props\n}: IVideoPlayerProps) => {\n const {\n refMedia,\n playing,\n currentTime,\n duration,\n muted: isMuted,\n toggle,\n seek,\n toggleMute,\n } = useMediaPlayer<HTMLVideoElement>({ onPlay, onPause, onEnded, onTimeUpdate });\n const refContainer = React.useRef<HTMLDivElement>(null);\n\n const PlaybackIcon = playing ? IconPlayerPauseFilled : IconPlayerPlayFilled;\n const VolumeIcon = isMuted ? IconVolumeOff : IconVolume;\n\n // An explicit height wins over the ratio, and either one turns the root into\n // the sized box with the media fitted inside it. Without both, the media is\n // the only thing in flow and the root takes its height.\n const isSizedBox = height != null || ratio !== 'auto';\n\n const requestFullscreen = () => {\n const container = refContainer.current;\n if (!container) {\n return;\n }\n if (document.fullscreenElement) {\n void document.exitFullscreen().catch(() => undefined);\n return;\n }\n void container.requestFullscreen?.().catch(() => undefined);\n };\n\n return (\n <div\n ref={refContainer}\n data-slot=\"video-player\"\n {...props}\n data-playing={playing || undefined}\n style={{\n width: cssLength(width),\n height: cssLength(height),\n aspectRatio: height == null && ratio !== 'auto' ? ratio : undefined,\n ...style,\n }}\n className={cn('dark relative isolate w-full overflow-hidden rounded-lg bg-background', className)}\n >\n <video\n ref={refMedia}\n src={src}\n poster={poster}\n muted={muted}\n loop={loop}\n playsInline\n preload=\"metadata\"\n aria-label={ariaLabel}\n className={cn('w-full bg-background', isSizedBox ? 'h-full object-contain' : 'h-auto')}\n />\n\n {/* The scrim must carry the controls over arbitrarily bright footage, so it ramps to a solid base rather than fading to 90%. */}\n {/* `text-foreground` is explicit: the `dark` class re-scopes the token VALUES, but `color` itself\n still inherits from the surrounding page, so without this the controls stay page-dark on video. */}\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 flex flex-col gap-0.5 bg-gradient-to-t from-background via-background/75 to-transparent px-3 pt-12 pb-2 text-foreground\">\n <div className=\"pointer-events-auto flex items-end justify-between gap-4\">\n <div className=\"min-w-0\">\n {title ? <p className=\"truncate text-sm font-medium text-foreground\">{title}</p> : null}\n {description ? <p className=\"truncate text-xs text-foreground/70\">{description}</p> : null}\n </div>\n <span className=\"shrink-0 text-xs tabular-nums text-foreground/70\">\n {formatMediaTime(currentTime)} / {formatMediaTime(duration)}\n </span>\n </div>\n\n {/* Scope the form-sized Slider down to a scrubber: thinner track, smaller thumb, less vertical padding. */}\n <Slider\n className=\"pointer-events-auto [&_[data-slot=slider-control]]:py-1.5 [&_[data-slot=slider-thumb]]:size-3 [&_[data-slot=slider-track]]:h-1\"\n value={Math.min(currentTime, duration || 0)}\n min={0}\n max={duration || 0}\n step={1}\n disabled={disabled || duration === 0}\n aria-label=\"Seek\"\n onValueChange={(next) => {\n seek(next);\n }}\n />\n\n <div className=\"pointer-events-auto flex items-center justify-between\">\n <div className=\"flex items-center gap-1\">\n <Button type=\"button\" variant=\"ghost\" size=\"icon\" disabled={disabled} aria-label={playing ? 'Pause' : 'Play'} onClick={toggle}>\n <PlaybackIcon />\n </Button>\n <Button type=\"button\" variant=\"ghost\" size=\"icon\" disabled={disabled} aria-label={isMuted ? 'Unmute' : 'Mute'} onClick={toggleMute}>\n <VolumeIcon />\n </Button>\n </div>\n <Button type=\"button\" variant=\"ghost\" size=\"icon\" disabled={disabled} aria-label=\"Fullscreen\" onClick={requestFullscreen}>\n <IconMaximize />\n </Button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;AA8DA,IAAa,eAAe,EAC1B,KACA,QACA,OACA,aACA,QAAQ,OACR,OAAO,OACP,WAAW,OACX,OACA,QACA,QAAQ,QACR,WACA,cAAc,YAAY,gBAC1B,OACA,QACA,SACA,SACA,cACA,GAAG,YACoB;CACvB,MAAM,EACJ,UACA,SACA,aACA,UACA,OAAO,SACP,QACA,MACA,eACE,eAAiC;EAAE;EAAQ;EAAS;EAAS;CAAa,CAAC;CAC/E,MAAM,eAAe,MAAM,OAAuB,IAAI;CAEtD,MAAM,eAAe,UAAU,wBAAwB;CACvD,MAAM,aAAa,UAAU,gBAAgB;CAK7C,MAAM,aAAa,UAAU,QAAQ,UAAU;CAE/C,MAAM,0BAA0B;EAC9B,MAAM,YAAY,aAAa;EAC/B,IAAI,CAAC,WACH;EAEF,IAAI,SAAS,mBAAmB;GAC9B,SAAc,eAAe,CAAC,CAAC,YAAY,KAAA,CAAS;GACpD;EACF;EACA,UAAe,oBAAoB,CAAC,CAAC,YAAY,KAAA,CAAS;CAC5D;CAEA,OACE,qBAAC,OAAD;EACE,KAAK;EACL,aAAU;EACV,GAAI;EACJ,gBAAc,WAAW,KAAA;EACzB,OAAO;GACL,OAAO,UAAU,KAAK;GACtB,QAAQ,UAAU,MAAM;GACxB,aAAa,UAAU,QAAQ,UAAU,SAAS,QAAQ,KAAA;GAC1D,GAAG;EACL;EACA,WAAW,GAAG,yEAAyE,SAAS;EAXlG,UAAA,CAaE,oBAAC,SAAD;GACE,KAAK;GACA;GACG;GACD;GACD;GACN,aAAA;GACA,SAAQ;GACR,cAAY;GACZ,WAAW,GAAG,wBAAwB,aAAa,0BAA0B,QAAQ;EACtF,CAAA,GAKD,qBAAC,OAAD;GAAK,WAAU;GAAf,UAAA;IACE,qBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,qBAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACG,QAAQ,oBAAC,KAAD;OAAG,WAAU;OAAgD,UAAA;MAAS,CAAA,IAAI,MAClF,cAAc,oBAAC,KAAD;OAAG,WAAU;OAAuC,UAAA;MAAe,CAAA,IAAI,IACnF;KACL,CAAA,GAAA,qBAAC,QAAD;MAAM,WAAU;MAAhB,UAAA;OACG,gBAAgB,WAAW;OAAE;OAAI,gBAAgB,QAAQ;MACtD;KACH,CAAA,CAAA;;IAGL,oBAAC,QAAD;KACE,WAAU;KACV,OAAO,KAAK,IAAI,aAAa,YAAY,CAAC;KAC1C,KAAK;KACL,KAAK,YAAY;KACjB,MAAM;KACN,UAAU,YAAY,aAAa;KACnC,cAAW;KACX,gBAAgB,SAAS;MACvB,KAAK,IAAI;KACX;IACD,CAAA;IAED,qBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,qBAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACE,oBAAC,QAAD;OAAQ,MAAK;OAAS,SAAQ;OAAQ,MAAK;OAAiB;OAAU,cAAY,UAAU,UAAU;OAAQ,SAAS;OACrH,UAAA,oBAAC,cAAD,CAAe,CAAA;MACT,CAAA,GACR,oBAAC,QAAD;OAAQ,MAAK;OAAS,SAAQ;OAAQ,MAAK;OAAiB;OAAU,cAAY,UAAU,WAAW;OAAQ,SAAS;OACtH,UAAA,oBAAC,YAAD,CAAa,CAAA;MACP,CAAA,CACL;KACL,CAAA,GAAA,oBAAC,QAAD;MAAQ,MAAK;MAAS,SAAQ;MAAQ,MAAK;MAAiB;MAAU,cAAW;MAAa,SAAS;MACrG,UAAA,oBAAC,cAAD,CAAe,CAAA;KACT,CAAA,CACL;;GACF;EACF,CAAA,CAAA;;AAET"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Accordion } from "@base-ui/react/accordion";
|
|
4
|
+
//#region ../ui/src/components/ui/accordion/accordion.tsx
|
|
5
|
+
var Accordion$1 = ({ value, defaultValue, onValueChange, multiple = false, className, children, ...props }) => /* @__PURE__ */ jsx(Accordion.Root, {
|
|
6
|
+
"data-slot": "accordion",
|
|
7
|
+
...props,
|
|
8
|
+
value,
|
|
9
|
+
defaultValue,
|
|
10
|
+
onValueChange: (next) => {
|
|
11
|
+
onValueChange?.(next);
|
|
12
|
+
},
|
|
13
|
+
multiple,
|
|
14
|
+
className: cn("w-full", className),
|
|
15
|
+
children
|
|
16
|
+
});
|
|
17
|
+
var AccordionItem = ({ value, disabled, className, children, ...props }) => /* @__PURE__ */ jsx(Accordion.Item, {
|
|
18
|
+
"data-slot": "accordion-item",
|
|
19
|
+
...props,
|
|
20
|
+
value,
|
|
21
|
+
disabled,
|
|
22
|
+
className: cn("border-b border-border", className),
|
|
23
|
+
children
|
|
24
|
+
});
|
|
25
|
+
var focusSiblingTrigger = (event) => {
|
|
26
|
+
if (![
|
|
27
|
+
"ArrowDown",
|
|
28
|
+
"ArrowUp",
|
|
29
|
+
"Home",
|
|
30
|
+
"End"
|
|
31
|
+
].includes(event.key)) return;
|
|
32
|
+
const root = event.currentTarget.closest("[data-slot=\"accordion\"]");
|
|
33
|
+
const triggers = Array.from(root?.querySelectorAll("[data-slot=\"accordion-trigger\"]:not(:disabled)") ?? []);
|
|
34
|
+
const currentIndex = triggers.indexOf(event.currentTarget);
|
|
35
|
+
if (currentIndex < 0 || triggers.length === 0) return;
|
|
36
|
+
event.preventDefault();
|
|
37
|
+
let nextIndex = currentIndex;
|
|
38
|
+
if (event.key === "Home") nextIndex = 0;
|
|
39
|
+
else if (event.key === "End") nextIndex = triggers.length - 1;
|
|
40
|
+
else if (event.key === "ArrowDown") nextIndex = (currentIndex + 1) % triggers.length;
|
|
41
|
+
else if (event.key === "ArrowUp") nextIndex = (currentIndex - 1 + triggers.length) % triggers.length;
|
|
42
|
+
triggers[nextIndex]?.focus();
|
|
43
|
+
};
|
|
44
|
+
var AccordionTrigger = ({ className, children, ...props }) => /* @__PURE__ */ jsx(Accordion.Header, {
|
|
45
|
+
"data-slot": "accordion-header",
|
|
46
|
+
className: "flex",
|
|
47
|
+
children: /* @__PURE__ */ jsxs(Accordion.Trigger, {
|
|
48
|
+
"data-slot": "accordion-trigger",
|
|
49
|
+
...props,
|
|
50
|
+
onKeyDown: focusSiblingTrigger,
|
|
51
|
+
className: cn("flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline", className),
|
|
52
|
+
children: [children, /* @__PURE__ */ jsx("svg", {
|
|
53
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
54
|
+
width: "24",
|
|
55
|
+
height: "24",
|
|
56
|
+
viewBox: "0 0 24 24",
|
|
57
|
+
fill: "none",
|
|
58
|
+
stroke: "currentColor",
|
|
59
|
+
strokeWidth: "2",
|
|
60
|
+
strokeLinecap: "round",
|
|
61
|
+
strokeLinejoin: "round",
|
|
62
|
+
className: "size-4 shrink-0 text-muted-foreground",
|
|
63
|
+
"data-expand-chevron": "flip",
|
|
64
|
+
"aria-hidden": "true",
|
|
65
|
+
children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
|
|
66
|
+
})]
|
|
67
|
+
})
|
|
68
|
+
});
|
|
69
|
+
var AccordionContent = ({ className, children, ...props }) => /* @__PURE__ */ jsx(Accordion.Panel, {
|
|
70
|
+
keepMounted: true,
|
|
71
|
+
"data-slot": "accordion-content",
|
|
72
|
+
...props,
|
|
73
|
+
"data-focus": "inset",
|
|
74
|
+
className: cn("text-sm", className),
|
|
75
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
76
|
+
className: "pb-4 pt-0",
|
|
77
|
+
children
|
|
78
|
+
})
|
|
79
|
+
});
|
|
80
|
+
//#endregion
|
|
81
|
+
export { Accordion$1 as Accordion, AccordionContent, AccordionItem, AccordionTrigger };
|
|
82
|
+
|
|
83
|
+
//# sourceMappingURL=accordion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accordion.js","names":[],"sources":["../../../../../../../ui/src/components/ui/accordion/accordion.tsx"],"sourcesContent":["import { Accordion as BaseAccordion } from '@base-ui/react/accordion';\nimport { cn } from '@edyn/ui/lib/utils';\nimport type * as React from 'react';\n\nexport interface IAccordionProps\n extends Omit<BaseAccordion.Root.Props, 'value' | 'defaultValue' | 'onValueChange' | 'multiple' | 'className' | 'children'> {\n value?: string[];\n defaultValue?: string[];\n onValueChange?: (value: string[]) => void;\n multiple?: boolean;\n className?: string;\n children: React.ReactNode;\n}\n\nexport const Accordion = ({ value, defaultValue, onValueChange, multiple = false, className, children, ...props }: IAccordionProps) => (\n <BaseAccordion.Root\n data-slot=\"accordion\"\n {...props}\n value={value}\n defaultValue={defaultValue}\n onValueChange={(next) => {\n onValueChange?.(next as string[]);\n }}\n multiple={multiple}\n className={cn('w-full', className)}\n >\n {children}\n </BaseAccordion.Root>\n);\n\nexport interface IAccordionItemProps extends Omit<BaseAccordion.Item.Props, 'value' | 'disabled' | 'className' | 'children'> {\n value: string;\n disabled?: boolean;\n className?: string;\n children: React.ReactNode;\n}\n\nexport const AccordionItem = ({ value, disabled, className, children, ...props }: IAccordionItemProps) => (\n <BaseAccordion.Item data-slot=\"accordion-item\" {...props} value={value} disabled={disabled} className={cn('border-b border-border', className)}>\n {children}\n </BaseAccordion.Item>\n);\n\nexport interface IAccordionTriggerProps extends Omit<BaseAccordion.Trigger.Props, 'className' | 'children'> {\n className?: string;\n children: React.ReactNode;\n}\n\nconst focusSiblingTrigger = (event: React.KeyboardEvent<HTMLButtonElement>): void => {\n const keys = ['ArrowDown', 'ArrowUp', 'Home', 'End'];\n if (!keys.includes(event.key)) {\n return;\n }\n\n const root = event.currentTarget.closest('[data-slot=\"accordion\"]');\n const triggers = Array.from(root?.querySelectorAll<HTMLButtonElement>('[data-slot=\"accordion-trigger\"]:not(:disabled)') ?? []);\n const currentIndex = triggers.indexOf(event.currentTarget);\n if (currentIndex < 0 || triggers.length === 0) {\n return;\n }\n\n event.preventDefault();\n let nextIndex = currentIndex;\n if (event.key === 'Home') {\n nextIndex = 0;\n } else if (event.key === 'End') {\n nextIndex = triggers.length - 1;\n } else if (event.key === 'ArrowDown') {\n nextIndex = (currentIndex + 1) % triggers.length;\n } else if (event.key === 'ArrowUp') {\n nextIndex = (currentIndex - 1 + triggers.length) % triggers.length;\n }\n triggers[nextIndex]?.focus();\n};\n\nexport const AccordionTrigger = ({ className, children, ...props }: IAccordionTriggerProps) => (\n <BaseAccordion.Header data-slot=\"accordion-header\" className=\"flex\">\n <BaseAccordion.Trigger\n data-slot=\"accordion-trigger\"\n {...props}\n onKeyDown={focusSiblingTrigger}\n className={cn('flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline', className)}\n >\n {children}\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"size-4 shrink-0 text-muted-foreground\"\n data-expand-chevron=\"flip\"\n aria-hidden=\"true\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </BaseAccordion.Trigger>\n </BaseAccordion.Header>\n);\n\nexport interface IAccordionContentProps extends Omit<BaseAccordion.Panel.Props, 'className' | 'children'> {\n className?: string;\n children: React.ReactNode;\n}\n\nexport const AccordionContent = ({ className, children, ...props }: IAccordionContentProps) => (\n <BaseAccordion.Panel\n keepMounted\n data-slot=\"accordion-content\"\n {...props}\n data-focus=\"inset\"\n // The height transition between Base UI's measured --accordion-panel-height\n // and 0 is what it detects and orchestrates; it lives in @edyn/theme/motion.css\n // so Collapsible and Accordion share one motion.\n className={cn('text-sm', className)}\n >\n <div className=\"pb-4 pt-0\">{children}</div>\n </BaseAccordion.Panel>\n);\n"],"mappings":";;;;AAcA,IAAa,eAAa,EAAE,OAAO,cAAc,eAAe,WAAW,OAAO,WAAW,UAAU,GAAG,YACxG,oBAAC,UAAc,MAAf;CACE,aAAU;CACV,GAAI;CACG;CACO;CACd,gBAAgB,SAAS;EACvB,gBAAgB,IAAgB;CAClC;CACU;CACV,WAAW,GAAG,UAAU,SAAS;CAEhC;AACiB,CAAA;AAUtB,IAAa,iBAAiB,EAAE,OAAO,UAAU,WAAW,UAAU,GAAG,YACvE,oBAAC,UAAc,MAAf;CAAoB,aAAU;CAAiB,GAAI;CAAc;CAAiB;CAAU,WAAW,GAAG,0BAA0B,SAAS;CAC1I;AACiB,CAAA;AAQtB,IAAM,uBAAuB,UAAwD;CAEnF,IAAI,CAAC;EADS;EAAa;EAAW;EAAQ;CACzC,CAAA,CAAK,SAAS,MAAM,GAAG,GAC1B;CAGF,MAAM,OAAO,MAAM,cAAc,QAAQ,2BAAyB;CAClE,MAAM,WAAW,MAAM,KAAK,MAAM,iBAAoC,kDAAgD,KAAK,CAAC,CAAC;CAC7H,MAAM,eAAe,SAAS,QAAQ,MAAM,aAAa;CACzD,IAAI,eAAe,KAAK,SAAS,WAAW,GAC1C;CAGF,MAAM,eAAe;CACrB,IAAI,YAAY;CAChB,IAAI,MAAM,QAAQ,QAChB,YAAY;MACP,IAAI,MAAM,QAAQ,OACvB,YAAY,SAAS,SAAS;MACzB,IAAI,MAAM,QAAQ,aACvB,aAAa,eAAe,KAAK,SAAS;MACrC,IAAI,MAAM,QAAQ,WACvB,aAAa,eAAe,IAAI,SAAS,UAAU,SAAS;CAE9D,SAAS,UAAU,EAAE,MAAM;AAC7B;AAEA,IAAa,oBAAoB,EAAE,WAAW,UAAU,GAAG,YACzD,oBAAC,UAAc,QAAf;CAAsB,aAAU;CAAmB,WAAU;CAC3D,UAAA,qBAAC,UAAc,SAAf;EACE,aAAU;EACV,GAAI;EACJ,WAAW;EACX,WAAW,GAAG,oGAAoG,SAAS;EAJ7H,UAAA,CAMG,UACD,oBAAC,OAAD;GACE,OAAM;GACN,OAAM;GACN,QAAO;GACP,SAAQ;GACR,MAAK;GACL,QAAO;GACP,aAAY;GACZ,eAAc;GACd,gBAAe;GACf,WAAU;GACV,uBAAoB;GACpB,eAAY;GAEZ,UAAA,oBAAC,QAAD,EAAM,GAAE,eAAgB,CAAA;EACrB,CAAA,CACgB;;AACH,CAAA;AAQxB,IAAa,oBAAoB,EAAE,WAAW,UAAU,GAAG,YACzD,oBAAC,UAAc,OAAf;CACE,aAAA;CACA,aAAU;CACV,GAAI;CACJ,cAAW;CAIX,WAAW,GAAG,WAAW,SAAS;CAElC,UAAA,oBAAC,OAAD;EAAK,WAAU;EAAa;CAAc,CAAA;AACvB,CAAA"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
//#region ../ui/src/components/ui/alert/alert.tsx
|
|
5
|
+
var alertVariants = cva("group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4", {
|
|
6
|
+
variants: { variant: {
|
|
7
|
+
default: "bg-card text-card-foreground",
|
|
8
|
+
destructive: "bg-card text-destructive *:data-[slot=alert-description]:text-destructive *:[svg]:text-current",
|
|
9
|
+
success: "bg-card text-success *:data-[slot=alert-description]:text-success *:[svg]:text-current",
|
|
10
|
+
warning: "bg-card text-warning *:data-[slot=alert-description]:text-warning *:[svg]:text-current",
|
|
11
|
+
info: "bg-card text-info *:data-[slot=alert-description]:text-info *:[svg]:text-current"
|
|
12
|
+
} },
|
|
13
|
+
defaultVariants: { variant: "default" }
|
|
14
|
+
});
|
|
15
|
+
var Alert = ({ className, variant, ...props }) => {
|
|
16
|
+
return /* @__PURE__ */ jsx("div", {
|
|
17
|
+
"data-slot": "alert",
|
|
18
|
+
role: "alert",
|
|
19
|
+
className: cn(alertVariants({ variant }), className),
|
|
20
|
+
...props
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
var AlertTitle = ({ className, ...props }) => {
|
|
24
|
+
return /* @__PURE__ */ jsx("div", {
|
|
25
|
+
"data-slot": "alert-title",
|
|
26
|
+
className: cn("font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground", className),
|
|
27
|
+
...props
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
var AlertDescription = ({ className, ...props }) => {
|
|
31
|
+
return /* @__PURE__ */ jsx("div", {
|
|
32
|
+
"data-slot": "alert-description",
|
|
33
|
+
className: cn("text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4", className),
|
|
34
|
+
...props
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
var AlertAction = ({ className, ...props }) => {
|
|
38
|
+
return /* @__PURE__ */ jsx("div", {
|
|
39
|
+
"data-slot": "alert-action",
|
|
40
|
+
className: cn("absolute top-2.5 right-2", className),
|
|
41
|
+
...props
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
export { Alert, AlertAction, AlertDescription, AlertTitle };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=alert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alert.js","names":[],"sources":["../../../../../../../ui/src/components/ui/alert/alert.tsx"],"sourcesContent":["import { cn } from '@edyn/ui/lib/utils';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport type * as React from 'react';\n\nconst alertVariants = cva(\n \"group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n default: 'bg-card text-card-foreground',\n destructive: 'bg-card text-destructive *:data-[slot=alert-description]:text-destructive *:[svg]:text-current',\n success: 'bg-card text-success *:data-[slot=alert-description]:text-success *:[svg]:text-current',\n warning: 'bg-card text-warning *:data-[slot=alert-description]:text-warning *:[svg]:text-current',\n info: 'bg-card text-info *:data-[slot=alert-description]:text-info *:[svg]:text-current',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n },\n);\n\nexport const Alert = ({ className, variant, ...props }: React.ComponentProps<'div'> & VariantProps<typeof alertVariants>) => {\n return <div data-slot=\"alert\" role=\"alert\" className={cn(alertVariants({ variant }), className)} {...props} />;\n};\n\nexport const AlertTitle = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return (\n <div\n data-slot=\"alert-title\"\n className={cn('font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground', className)}\n {...props}\n />\n );\n};\n\nexport const AlertDescription = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return (\n <div\n data-slot=\"alert-description\"\n className={cn(\n 'text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4',\n className,\n )}\n {...props}\n />\n );\n};\n\nexport const AlertAction = ({ className, ...props }: React.ComponentProps<'div'>) => {\n return <div data-slot=\"alert-action\" className={cn('absolute top-2.5 right-2', className)} {...props} />;\n};\n"],"mappings":";;;;AAIA,IAAM,gBAAgB,IACpB,6TACA;CACE,UAAU,EACR,SAAS;EACP,SAAS;EACT,aAAa;EACb,SAAS;EACT,SAAS;EACT,MAAM;CACR,EACF;CACA,iBAAiB,EACf,SAAS,UACX;AACF,CACF;AAEA,IAAa,SAAS,EAAE,WAAW,SAAS,GAAG,YAA8E;CAC3H,OAAO,oBAAC,OAAD;EAAK,aAAU;EAAQ,MAAK;EAAQ,WAAW,GAAG,cAAc,EAAE,QAAQ,CAAC,GAAG,SAAS;EAAG,GAAI;CAAQ,CAAA;AAC/G;AAEA,IAAa,cAAc,EAAE,WAAW,GAAG,YAAyC;CAClF,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,uHAAuH,SAAS;EAC9I,GAAI;CACL,CAAA;AAEL;AAEA,IAAa,oBAAoB,EAAE,WAAW,GAAG,YAAyC;CACxF,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,8JACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,IAAa,eAAe,EAAE,WAAW,GAAG,YAAyC;CACnF,OAAO,oBAAC,OAAD;EAAK,aAAU;EAAe,WAAW,GAAG,4BAA4B,SAAS;EAAG,GAAI;CAAQ,CAAA;AACzG"}
|