@12-apps/ui 6.10.0 → 6.10.1
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/TextareaRichEditor-ZFB44VWZ.js +303 -0
- package/dist/TextareaRichEditor-ZFB44VWZ.js.map +1 -0
- package/dist/chunk-PQWPMIGH.js +94 -0
- package/dist/chunk-PQWPMIGH.js.map +1 -0
- package/dist/form/Textarea.js +46 -414
- package/dist/form/Textarea.js.map +1 -1
- package/dist/types/components/form/Textarea/Textarea.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/form/Textarea/Textarea.tsx +60 -14
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import {
|
|
2
|
+
floatAnimation,
|
|
3
|
+
getColorFromTheme
|
|
4
|
+
} from "./chunk-PQWPMIGH.js";
|
|
5
|
+
import "./chunk-PZ5AY32C.js";
|
|
6
|
+
|
|
7
|
+
// src/components/form/Textarea/TextareaRichEditor.tsx
|
|
8
|
+
import Code from "@mui/icons-material/Code";
|
|
9
|
+
import FormatBold from "@mui/icons-material/FormatBold";
|
|
10
|
+
import FormatColorFill from "@mui/icons-material/FormatColorFill";
|
|
11
|
+
import FormatColorText from "@mui/icons-material/FormatColorText";
|
|
12
|
+
import FormatItalic from "@mui/icons-material/FormatItalic";
|
|
13
|
+
import FormatListBulleted from "@mui/icons-material/FormatListBulleted";
|
|
14
|
+
import FormatListNumbered from "@mui/icons-material/FormatListNumbered";
|
|
15
|
+
import FormatQuote from "@mui/icons-material/FormatQuote";
|
|
16
|
+
import FormatUnderlined from "@mui/icons-material/FormatUnderlined";
|
|
17
|
+
import Link from "@mui/icons-material/Link";
|
|
18
|
+
import { alpha, Box, Divider, IconButton, Tooltip } from "@mui/material";
|
|
19
|
+
import { styled } from "@mui/material";
|
|
20
|
+
import { useEffect, useRef, useState } from "react";
|
|
21
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
22
|
+
var RichToolbar = styled(Box)(({ theme, glass }) => ({
|
|
23
|
+
display: "flex",
|
|
24
|
+
alignItems: "center",
|
|
25
|
+
gap: theme.spacing(0.5),
|
|
26
|
+
padding: theme.spacing(1),
|
|
27
|
+
borderRadius: `${theme.spacing(1)} ${theme.spacing(1)} 0 0`,
|
|
28
|
+
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
29
|
+
background: glass ? alpha(theme.palette.background.paper, 0.1) : theme.palette.background.paper,
|
|
30
|
+
...glass && {
|
|
31
|
+
backdropFilter: "blur(15px)"
|
|
32
|
+
},
|
|
33
|
+
"& .MuiDivider-root": {
|
|
34
|
+
height: 24,
|
|
35
|
+
margin: `0 ${theme.spacing(1)}`
|
|
36
|
+
}
|
|
37
|
+
}));
|
|
38
|
+
var ToolbarButton = styled(IconButton, {
|
|
39
|
+
shouldForwardProp: (prop) => prop !== "active"
|
|
40
|
+
})(({ theme, active }) => ({
|
|
41
|
+
padding: theme.spacing(0.75),
|
|
42
|
+
borderRadius: theme.spacing(0.5),
|
|
43
|
+
color: active ? theme.palette.primary.main : theme.palette.text.secondary,
|
|
44
|
+
backgroundColor: active ? alpha(theme.palette.primary.main, 0.1) : "transparent",
|
|
45
|
+
transition: "all 0.2s ease",
|
|
46
|
+
position: "relative",
|
|
47
|
+
overflow: "hidden",
|
|
48
|
+
"&::before": {
|
|
49
|
+
content: '""',
|
|
50
|
+
position: "absolute",
|
|
51
|
+
top: "50%",
|
|
52
|
+
left: "50%",
|
|
53
|
+
width: 0,
|
|
54
|
+
height: 0,
|
|
55
|
+
borderRadius: "50%",
|
|
56
|
+
backgroundColor: alpha(theme.palette.primary.main, 0.3),
|
|
57
|
+
transform: "translate(-50%, -50%)",
|
|
58
|
+
transition: "width 0.3s, height 0.3s"
|
|
59
|
+
},
|
|
60
|
+
"&:hover": {
|
|
61
|
+
color: theme.palette.primary.main,
|
|
62
|
+
backgroundColor: alpha(theme.palette.primary.main, 0.15),
|
|
63
|
+
transform: "translateY(-1px)",
|
|
64
|
+
animation: `${floatAnimation} 1.5s ease-in-out infinite`,
|
|
65
|
+
"&::before": {
|
|
66
|
+
width: "100%",
|
|
67
|
+
height: "100%"
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"&:active": {
|
|
71
|
+
transform: "scale(0.95)"
|
|
72
|
+
}
|
|
73
|
+
}));
|
|
74
|
+
var CharacterCount = styled(Box)(({ theme, limit, count }) => {
|
|
75
|
+
const isWarning = limit && count > limit * 0.8;
|
|
76
|
+
const isError = limit && count > limit;
|
|
77
|
+
return {
|
|
78
|
+
position: "absolute",
|
|
79
|
+
bottom: theme.spacing(1),
|
|
80
|
+
right: theme.spacing(1),
|
|
81
|
+
fontSize: "0.75rem",
|
|
82
|
+
color: isError ? theme.palette.error.main : isWarning ? theme.palette.warning.main : theme.palette.text.secondary,
|
|
83
|
+
padding: "2px 6px",
|
|
84
|
+
borderRadius: theme.spacing(0.5),
|
|
85
|
+
backgroundColor: alpha(theme.palette.background.paper, 0.8),
|
|
86
|
+
backdropFilter: "blur(10px)",
|
|
87
|
+
transition: "all 0.3s ease"
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
var ContentEditableDiv = styled("div")(({ theme, error, focused, glass, customColor = "primary" }) => {
|
|
91
|
+
if (!theme) return {};
|
|
92
|
+
const colorPalette = getColorFromTheme(theme, customColor);
|
|
93
|
+
const errorColor = theme.palette.error;
|
|
94
|
+
return {
|
|
95
|
+
minHeight: "120px",
|
|
96
|
+
padding: theme.spacing(1.5),
|
|
97
|
+
borderRadius: `0 0 ${theme.spacing(1)} ${theme.spacing(1)}`,
|
|
98
|
+
border: `2px solid ${error ? errorColor.main : focused ? colorPalette.main : theme.palette.divider}`,
|
|
99
|
+
borderTop: "none",
|
|
100
|
+
backgroundColor: glass ? alpha(theme.palette.background.paper, 0.1) : theme.palette.background.paper,
|
|
101
|
+
color: theme.palette.text.primary,
|
|
102
|
+
fontFamily: theme.typography.fontFamily,
|
|
103
|
+
fontSize: "1rem",
|
|
104
|
+
lineHeight: 1.5,
|
|
105
|
+
outline: "none",
|
|
106
|
+
transition: "all 0.3s ease",
|
|
107
|
+
cursor: "text",
|
|
108
|
+
...glass && {
|
|
109
|
+
backdropFilter: "blur(20px)"
|
|
110
|
+
},
|
|
111
|
+
"&:hover": {
|
|
112
|
+
backgroundColor: glass ? alpha(theme.palette.background.paper, 0.15) : alpha(theme.palette.background.paper, 0.9)
|
|
113
|
+
},
|
|
114
|
+
"&:focus": {
|
|
115
|
+
borderColor: error ? errorColor.main : colorPalette.main,
|
|
116
|
+
boxShadow: `0 0 0 3px ${alpha(error ? errorColor.main : colorPalette.main, 0.1)}`
|
|
117
|
+
},
|
|
118
|
+
"& > *": {
|
|
119
|
+
margin: "0.5em 0"
|
|
120
|
+
},
|
|
121
|
+
'&[contenteditable="true"]:empty::before': {
|
|
122
|
+
content: "attr(data-placeholder)",
|
|
123
|
+
color: theme.palette.text.secondary,
|
|
124
|
+
opacity: 0.6
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
var formatText = (command, value) => {
|
|
129
|
+
document.execCommand(command, false, value);
|
|
130
|
+
};
|
|
131
|
+
var useRichEditorState = ({
|
|
132
|
+
value,
|
|
133
|
+
onChange
|
|
134
|
+
}) => {
|
|
135
|
+
const [focused, setFocused] = useState(false);
|
|
136
|
+
const [characterCount, setCharacterCount] = useState(0);
|
|
137
|
+
const [activeFormats, setActiveFormats] = useState({
|
|
138
|
+
bold: false,
|
|
139
|
+
italic: false,
|
|
140
|
+
underline: false,
|
|
141
|
+
list: false,
|
|
142
|
+
orderedList: false,
|
|
143
|
+
quote: false,
|
|
144
|
+
code: false
|
|
145
|
+
});
|
|
146
|
+
const contentRef = useRef(null);
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
if (contentRef.current && value !== void 0) {
|
|
149
|
+
if (contentRef.current.innerHTML !== value) {
|
|
150
|
+
contentRef.current.innerHTML = value;
|
|
151
|
+
}
|
|
152
|
+
setCharacterCount(contentRef.current.innerText.length);
|
|
153
|
+
}
|
|
154
|
+
}, [value]);
|
|
155
|
+
const handleInput = () => {
|
|
156
|
+
if (contentRef.current) {
|
|
157
|
+
const html = contentRef.current.innerHTML;
|
|
158
|
+
onChange?.(html);
|
|
159
|
+
setCharacterCount(contentRef.current.innerText.length);
|
|
160
|
+
updateActiveFormats();
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const updateActiveFormats = () => {
|
|
164
|
+
setActiveFormats({
|
|
165
|
+
bold: document.queryCommandState("bold"),
|
|
166
|
+
italic: document.queryCommandState("italic"),
|
|
167
|
+
underline: document.queryCommandState("underline"),
|
|
168
|
+
list: document.queryCommandState("insertUnorderedList"),
|
|
169
|
+
orderedList: document.queryCommandState("insertOrderedList"),
|
|
170
|
+
quote: false,
|
|
171
|
+
code: false
|
|
172
|
+
});
|
|
173
|
+
};
|
|
174
|
+
const handleFormat = (command, value2) => {
|
|
175
|
+
formatText(command, value2);
|
|
176
|
+
updateActiveFormats();
|
|
177
|
+
contentRef.current?.focus();
|
|
178
|
+
};
|
|
179
|
+
return {
|
|
180
|
+
focused,
|
|
181
|
+
setFocused,
|
|
182
|
+
characterCount,
|
|
183
|
+
activeFormats,
|
|
184
|
+
contentRef,
|
|
185
|
+
handleInput,
|
|
186
|
+
handleFormat,
|
|
187
|
+
updateActiveFormats
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
var InlineFormatButtons = ({ copy, activeFormats, onCommand }) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
191
|
+
/* @__PURE__ */ jsx(Tooltip, { title: copy.bold, arrow: true, children: /* @__PURE__ */ jsx(
|
|
192
|
+
ToolbarButton,
|
|
193
|
+
{
|
|
194
|
+
size: "small",
|
|
195
|
+
active: activeFormats.bold,
|
|
196
|
+
onClick: () => onCommand("bold"),
|
|
197
|
+
children: /* @__PURE__ */ jsx(FormatBold, { fontSize: "small" })
|
|
198
|
+
}
|
|
199
|
+
) }),
|
|
200
|
+
/* @__PURE__ */ jsx(Tooltip, { title: copy.italic, arrow: true, children: /* @__PURE__ */ jsx(
|
|
201
|
+
ToolbarButton,
|
|
202
|
+
{
|
|
203
|
+
size: "small",
|
|
204
|
+
active: activeFormats.italic,
|
|
205
|
+
onClick: () => onCommand("italic"),
|
|
206
|
+
children: /* @__PURE__ */ jsx(FormatItalic, { fontSize: "small" })
|
|
207
|
+
}
|
|
208
|
+
) }),
|
|
209
|
+
/* @__PURE__ */ jsx(Tooltip, { title: copy.underline, arrow: true, children: /* @__PURE__ */ jsx(
|
|
210
|
+
ToolbarButton,
|
|
211
|
+
{
|
|
212
|
+
size: "small",
|
|
213
|
+
active: activeFormats.underline,
|
|
214
|
+
onClick: () => onCommand("underline"),
|
|
215
|
+
children: /* @__PURE__ */ jsx(FormatUnderlined, { fontSize: "small" })
|
|
216
|
+
}
|
|
217
|
+
) })
|
|
218
|
+
] });
|
|
219
|
+
var BlockFormatButtons = ({ copy, activeFormats, onCommand }) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
220
|
+
/* @__PURE__ */ jsx(Divider, { orientation: "vertical", flexItem: true }),
|
|
221
|
+
/* @__PURE__ */ jsx(Tooltip, { title: copy.bulletList, arrow: true, children: /* @__PURE__ */ jsx(
|
|
222
|
+
ToolbarButton,
|
|
223
|
+
{
|
|
224
|
+
size: "small",
|
|
225
|
+
active: activeFormats.list,
|
|
226
|
+
onClick: () => onCommand("insertUnorderedList"),
|
|
227
|
+
children: /* @__PURE__ */ jsx(FormatListBulleted, { fontSize: "small" })
|
|
228
|
+
}
|
|
229
|
+
) }),
|
|
230
|
+
/* @__PURE__ */ jsx(Tooltip, { title: copy.numberedList, arrow: true, children: /* @__PURE__ */ jsx(
|
|
231
|
+
ToolbarButton,
|
|
232
|
+
{
|
|
233
|
+
size: "small",
|
|
234
|
+
active: activeFormats.orderedList,
|
|
235
|
+
onClick: () => onCommand("insertOrderedList"),
|
|
236
|
+
children: /* @__PURE__ */ jsx(FormatListNumbered, { fontSize: "small" })
|
|
237
|
+
}
|
|
238
|
+
) }),
|
|
239
|
+
/* @__PURE__ */ jsx(Divider, { orientation: "vertical", flexItem: true }),
|
|
240
|
+
/* @__PURE__ */ jsx(Tooltip, { title: copy.quote, arrow: true, children: /* @__PURE__ */ jsx(ToolbarButton, { size: "small", onClick: () => onCommand("formatBlock", "blockquote"), children: /* @__PURE__ */ jsx(FormatQuote, { fontSize: "small" }) }) }),
|
|
241
|
+
/* @__PURE__ */ jsx(Tooltip, { title: copy.code, arrow: true, children: /* @__PURE__ */ jsx(ToolbarButton, { size: "small", onClick: () => onCommand("formatBlock", "pre"), children: /* @__PURE__ */ jsx(Code, { fontSize: "small" }) }) }),
|
|
242
|
+
/* @__PURE__ */ jsx(Divider, { orientation: "vertical", flexItem: true }),
|
|
243
|
+
/* @__PURE__ */ jsx(Tooltip, { title: copy.insertLink, arrow: true, children: /* @__PURE__ */ jsx(
|
|
244
|
+
ToolbarButton,
|
|
245
|
+
{
|
|
246
|
+
size: "small",
|
|
247
|
+
onClick: () => {
|
|
248
|
+
const url = window.prompt(copy.linkPrompt);
|
|
249
|
+
if (url) onCommand("createLink", url);
|
|
250
|
+
},
|
|
251
|
+
children: /* @__PURE__ */ jsx(Link, { fontSize: "small" })
|
|
252
|
+
}
|
|
253
|
+
) }),
|
|
254
|
+
/* @__PURE__ */ jsx(Tooltip, { title: copy.textColor, arrow: true, children: /* @__PURE__ */ jsx(ToolbarButton, { size: "small", children: /* @__PURE__ */ jsx(FormatColorText, { fontSize: "small" }) }) }),
|
|
255
|
+
/* @__PURE__ */ jsx(Tooltip, { title: copy.backgroundColor, arrow: true, children: /* @__PURE__ */ jsx(ToolbarButton, { size: "small", children: /* @__PURE__ */ jsx(FormatColorFill, { fontSize: "small" }) }) })
|
|
256
|
+
] });
|
|
257
|
+
var EditorToolbar = ({ copy, glass, activeFormats, onCommand }) => /* @__PURE__ */ jsxs(RichToolbar, { glass, children: [
|
|
258
|
+
/* @__PURE__ */ jsx(InlineFormatButtons, { copy, activeFormats, onCommand }),
|
|
259
|
+
/* @__PURE__ */ jsx(BlockFormatButtons, { copy, activeFormats, onCommand })
|
|
260
|
+
] });
|
|
261
|
+
var TextareaRichEditor = ({ copy, value, onChange, placeholder, error, glass, color, characterLimit }) => {
|
|
262
|
+
const {
|
|
263
|
+
focused,
|
|
264
|
+
setFocused,
|
|
265
|
+
characterCount,
|
|
266
|
+
activeFormats,
|
|
267
|
+
contentRef,
|
|
268
|
+
handleInput,
|
|
269
|
+
handleFormat,
|
|
270
|
+
updateActiveFormats
|
|
271
|
+
} = useRichEditorState({ value, onChange });
|
|
272
|
+
return /* @__PURE__ */ jsxs(Box, { sx: { position: "relative" }, children: [
|
|
273
|
+
/* @__PURE__ */ jsx(EditorToolbar, { copy, glass, activeFormats, onCommand: handleFormat }),
|
|
274
|
+
/* @__PURE__ */ jsx(
|
|
275
|
+
ContentEditableDiv,
|
|
276
|
+
{
|
|
277
|
+
ref: contentRef,
|
|
278
|
+
contentEditable: true,
|
|
279
|
+
"data-placeholder": placeholder,
|
|
280
|
+
error,
|
|
281
|
+
focused,
|
|
282
|
+
glass,
|
|
283
|
+
customColor: color,
|
|
284
|
+
onFocus: () => setFocused(true),
|
|
285
|
+
onBlur: () => setFocused(false),
|
|
286
|
+
onInput: handleInput,
|
|
287
|
+
onKeyUp: updateActiveFormats,
|
|
288
|
+
onMouseUp: updateActiveFormats,
|
|
289
|
+
role: "textbox",
|
|
290
|
+
"aria-multiline": "true",
|
|
291
|
+
"aria-label": placeholder || "Rich text editor"
|
|
292
|
+
}
|
|
293
|
+
),
|
|
294
|
+
characterLimit && /* @__PURE__ */ jsxs(CharacterCount, { limit: characterLimit, count: characterCount, children: [
|
|
295
|
+
characterCount,
|
|
296
|
+
characterLimit ? `/${characterLimit}` : ""
|
|
297
|
+
] })
|
|
298
|
+
] });
|
|
299
|
+
};
|
|
300
|
+
export {
|
|
301
|
+
TextareaRichEditor
|
|
302
|
+
};
|
|
303
|
+
//# sourceMappingURL=TextareaRichEditor-ZFB44VWZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/form/Textarea/TextareaRichEditor.tsx"],"sourcesContent":["import Code from '@mui/icons-material/Code';\nimport FormatBold from '@mui/icons-material/FormatBold';\nimport FormatColorFill from '@mui/icons-material/FormatColorFill';\nimport FormatColorText from '@mui/icons-material/FormatColorText';\nimport FormatItalic from '@mui/icons-material/FormatItalic';\nimport FormatListBulleted from '@mui/icons-material/FormatListBulleted';\nimport FormatListNumbered from '@mui/icons-material/FormatListNumbered';\nimport FormatQuote from '@mui/icons-material/FormatQuote';\nimport FormatUnderlined from '@mui/icons-material/FormatUnderlined';\nimport Link from '@mui/icons-material/Link';\nimport { alpha, Box, Divider, IconButton, Tooltip } from '@mui/material';\nimport type { Theme } from '@mui/material';\nimport { styled } from '@mui/material';\nimport React, { useEffect, useRef, useState } from 'react';\n\nimport { floatAnimation, getColorFromTheme } from './Textarea.styles';\nimport type { RichEditorToolbarCopy } from '../../../copy';\n\nconst RichToolbar = styled(Box)<{ glass?: boolean }>(({ theme, glass }) => ({\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(0.5),\n padding: theme.spacing(1),\n borderRadius: `${theme.spacing(1)} ${theme.spacing(1)} 0 0`,\n borderBottom: `1px solid ${theme.palette.divider}`,\n background: glass ? alpha(theme.palette.background.paper, 0.1) : theme.palette.background.paper,\n ...(glass && {\n backdropFilter: 'blur(15px)' }),\n '& .MuiDivider-root': {\n height: 24,\n margin: `0 ${theme.spacing(1)}` } }));\n\nconst ToolbarButton = styled(IconButton, {\n shouldForwardProp: (prop) => prop !== 'active' })<{ active?: boolean }>(({ theme, active }) => ({\n padding: theme.spacing(0.75),\n borderRadius: theme.spacing(0.5),\n color: active ? theme.palette.primary.main : theme.palette.text.secondary,\n backgroundColor: active ? alpha(theme.palette.primary.main, 0.1) : 'transparent',\n transition: 'all 0.2s ease',\n position: 'relative',\n overflow: 'hidden',\n\n '&::before': {\n content: '\"\"',\n position: 'absolute',\n top: '50%',\n left: '50%',\n width: 0,\n height: 0,\n borderRadius: '50%',\n backgroundColor: alpha(theme.palette.primary.main, 0.3),\n transform: 'translate(-50%, -50%)',\n transition: 'width 0.3s, height 0.3s' },\n\n '&:hover': {\n color: theme.palette.primary.main,\n backgroundColor: alpha(theme.palette.primary.main, 0.15),\n transform: 'translateY(-1px)',\n animation: `${floatAnimation} 1.5s ease-in-out infinite`,\n\n '&::before': {\n width: '100%',\n height: '100%' } },\n\n '&:active': {\n transform: 'scale(0.95)' } }));\n\nconst CharacterCount = styled(Box)<{ limit?: number; count: number }>(({ theme, limit, count }) => {\n const isWarning = limit && count > limit * 0.8;\n const isError = limit && count > limit;\n\n return {\n position: 'absolute',\n bottom: theme.spacing(1),\n right: theme.spacing(1),\n fontSize: '0.75rem',\n color: isError\n ? theme.palette.error.main\n : isWarning\n ? theme.palette.warning.main\n : theme.palette.text.secondary,\n padding: '2px 6px',\n borderRadius: theme.spacing(0.5),\n backgroundColor: alpha(theme.palette.background.paper, 0.8),\n backdropFilter: 'blur(10px)',\n transition: 'all 0.3s ease' };\n});\n\nconst ContentEditableDiv = styled('div')<{\n error?: boolean;\n focused?: boolean;\n glass?: boolean;\n customColor?: string;\n theme?: Theme;\n}>(({ theme, error, focused, glass, customColor = 'primary' }) => {\n if (!theme) return {};\n const colorPalette = getColorFromTheme(theme, customColor);\n const errorColor = theme.palette.error;\n\n return {\n minHeight: '120px',\n padding: theme.spacing(1.5),\n borderRadius: `0 0 ${theme.spacing(1)} ${theme.spacing(1)}`,\n border: `2px solid ${error ? errorColor.main : focused ? colorPalette.main : theme.palette.divider}`,\n borderTop: 'none',\n backgroundColor: glass\n ? alpha(theme.palette.background.paper, 0.1)\n : theme.palette.background.paper,\n color: theme.palette.text.primary,\n fontFamily: theme.typography.fontFamily,\n fontSize: '1rem',\n lineHeight: 1.5,\n outline: 'none',\n transition: 'all 0.3s ease',\n cursor: 'text',\n\n ...(glass && {\n backdropFilter: 'blur(20px)' }),\n\n '&:hover': {\n backgroundColor: glass\n ? alpha(theme.palette.background.paper, 0.15)\n : alpha(theme.palette.background.paper, 0.9) },\n\n '&:focus': {\n borderColor: error ? errorColor.main : colorPalette.main,\n boxShadow: `0 0 0 3px ${alpha(error ? errorColor.main : colorPalette.main, 0.1)}` },\n\n '& > *': {\n margin: '0.5em 0' },\n\n '&[contenteditable=\"true\"]:empty::before': {\n content: 'attr(data-placeholder)',\n color: theme.palette.text.secondary,\n opacity: 0.6 } };\n});\n\n// Rich text formatting functions\nconst formatText = (command: string, value?: string) => {\n document.execCommand(command, false, value);\n};\n\n// Editable state: what is typed, how long it is, and which formats apply at the\n// caret. Kept apart from the markup below.\nconst useRichEditorState = ({\n value,\n onChange }: {\n value?: string;\n onChange?: (html: string) => void;\n}) => {\n const [focused, setFocused] = useState(false);\n const [characterCount, setCharacterCount] = useState(0);\n const [activeFormats, setActiveFormats] = useState({\n bold: false,\n italic: false,\n underline: false,\n list: false,\n orderedList: false,\n quote: false,\n code: false });\n const contentRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (contentRef.current && value !== undefined) {\n if (contentRef.current.innerHTML !== value) {\n contentRef.current.innerHTML = value;\n }\n setCharacterCount(contentRef.current.innerText.length);\n }\n }, [value]);\n\n const handleInput = () => {\n if (contentRef.current) {\n const html = contentRef.current.innerHTML;\n onChange?.(html);\n setCharacterCount(contentRef.current.innerText.length);\n updateActiveFormats();\n }\n };\n\n const updateActiveFormats = () => {\n setActiveFormats({\n bold: document.queryCommandState('bold'),\n italic: document.queryCommandState('italic'),\n underline: document.queryCommandState('underline'),\n list: document.queryCommandState('insertUnorderedList'),\n orderedList: document.queryCommandState('insertOrderedList'),\n quote: false,\n code: false });\n };\n\n const handleFormat = (command: string, value?: string) => {\n formatText(command, value);\n updateActiveFormats();\n contentRef.current?.focus();\n };\n\n return {\n focused,\n setFocused,\n characterCount,\n activeFormats,\n contentRef,\n handleInput,\n handleFormat,\n updateActiveFormats };\n};\n\nconst InlineFormatButtons: React.FC<{\n copy: RichEditorToolbarCopy;\n activeFormats: Record<string, boolean>;\n onCommand: (command: string, value?: string) => void;\n}> = ({ copy, activeFormats, onCommand }) => (\n <>\n <Tooltip title={copy.bold} arrow>\n <ToolbarButton\n size=\"small\"\n active={activeFormats.bold}\n onClick={() => onCommand('bold')}\n >\n <FormatBold fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Tooltip title={copy.italic} arrow>\n <ToolbarButton\n size=\"small\"\n active={activeFormats.italic}\n onClick={() => onCommand('italic')}\n >\n <FormatItalic fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Tooltip title={copy.underline} arrow>\n <ToolbarButton\n size=\"small\"\n active={activeFormats.underline}\n onClick={() => onCommand('underline')}\n >\n <FormatUnderlined fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n </>\n);\n\nconst BlockFormatButtons: React.FC<{\n copy: RichEditorToolbarCopy;\n activeFormats: Record<string, boolean>;\n onCommand: (command: string, value?: string) => void;\n}> = ({ copy, activeFormats, onCommand }) => (\n <>\n <Divider orientation=\"vertical\" flexItem />\n\n <Tooltip title={copy.bulletList} arrow>\n <ToolbarButton\n size=\"small\"\n active={activeFormats.list}\n onClick={() => onCommand('insertUnorderedList')}\n >\n <FormatListBulleted fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Tooltip title={copy.numberedList} arrow>\n <ToolbarButton\n size=\"small\"\n active={activeFormats.orderedList}\n onClick={() => onCommand('insertOrderedList')}\n >\n <FormatListNumbered fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Divider orientation=\"vertical\" flexItem />\n\n <Tooltip title={copy.quote} arrow>\n <ToolbarButton size=\"small\" onClick={() => onCommand('formatBlock', 'blockquote')}>\n <FormatQuote fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Tooltip title={copy.code} arrow>\n <ToolbarButton size=\"small\" onClick={() => onCommand('formatBlock', 'pre')}>\n <Code fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Divider orientation=\"vertical\" flexItem />\n\n <Tooltip title={copy.insertLink} arrow>\n <ToolbarButton\n size=\"small\"\n onClick={() => {\n const url = window.prompt(copy.linkPrompt);\n if (url) onCommand('createLink', url);\n }}\n >\n <Link fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Tooltip title={copy.textColor} arrow>\n <ToolbarButton size=\"small\">\n <FormatColorText fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Tooltip title={copy.backgroundColor} arrow>\n <ToolbarButton size=\"small\">\n <FormatColorFill fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n </>\n);\n\n// The formatting buttons. Split out so the editor body is state, handlers and\n// the editable region.\nconst EditorToolbar: React.FC<{\n copy: RichEditorToolbarCopy;\n glass?: boolean;\n activeFormats: Record<string, boolean>;\n onCommand: (command: string, value?: string) => void;\n}> = ({ copy, glass, activeFormats, onCommand }) => (\n <RichToolbar glass={glass}>\n <InlineFormatButtons copy={copy} activeFormats={activeFormats} onCommand={onCommand} />\n <BlockFormatButtons copy={copy} activeFormats={activeFormats} onCommand={onCommand} />\n </RichToolbar>\n);\n\nexport const TextareaRichEditor: React.FC<{\n /**\n * The ten toolbar buttons' tooltips — which are also their accessible names,\n * since every one of them is a glyph — plus the link prompt. REQUIRED: this\n * package ships no default copy.\n */\n copy: RichEditorToolbarCopy;\n value?: string;\n onChange?: (html: string) => void;\n placeholder?: string;\n error?: boolean;\n glass?: boolean;\n color?: string;\n characterLimit?: number;\n}> = ({ copy, value, onChange, placeholder, error, glass, color, characterLimit }) => {\n const {\n focused,\n setFocused,\n characterCount,\n activeFormats,\n contentRef,\n handleInput,\n handleFormat,\n updateActiveFormats } = useRichEditorState({ value, onChange });\n\n return (\n <Box sx={{ position: 'relative' }}>\n <EditorToolbar copy={copy} glass={glass} activeFormats={activeFormats} onCommand={handleFormat} />\n\n <ContentEditableDiv\n ref={contentRef}\n contentEditable\n data-placeholder={placeholder}\n error={error}\n focused={focused}\n glass={glass}\n customColor={color}\n onFocus={() => setFocused(true)}\n onBlur={() => setFocused(false)}\n onInput={handleInput}\n onKeyUp={updateActiveFormats}\n onMouseUp={updateActiveFormats}\n role=\"textbox\"\n aria-multiline=\"true\"\n aria-label={placeholder || 'Rich text editor'}\n />\n\n {characterLimit && (\n <CharacterCount limit={characterLimit} count={characterCount}>\n {characterCount}\n {characterLimit ? `/${characterLimit}` : ''}\n </CharacterCount>\n )}\n </Box>\n );\n};\n"],"mappings":"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","names":["value"]}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// src/components/form/Textarea/Textarea.styles.ts
|
|
2
|
+
import { alpha, keyframes } from "@mui/material";
|
|
3
|
+
var glowAnimation = keyframes`
|
|
4
|
+
0% {
|
|
5
|
+
box-shadow: 0 0 5px currentColor;
|
|
6
|
+
}
|
|
7
|
+
50% {
|
|
8
|
+
box-shadow: 0 0 20px currentColor, 0 0 30px currentColor;
|
|
9
|
+
}
|
|
10
|
+
100% {
|
|
11
|
+
box-shadow: 0 0 5px currentColor;
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
14
|
+
var floatAnimation = keyframes`
|
|
15
|
+
0% {
|
|
16
|
+
transform: translateY(0px);
|
|
17
|
+
}
|
|
18
|
+
50% {
|
|
19
|
+
transform: translateY(-3px);
|
|
20
|
+
}
|
|
21
|
+
100% {
|
|
22
|
+
transform: translateY(0px);
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
var neutralPalette = (theme) => {
|
|
26
|
+
const grey = theme.palette.grey;
|
|
27
|
+
return {
|
|
28
|
+
main: grey?.[700] || "#616161",
|
|
29
|
+
dark: grey?.[800] || "#424242",
|
|
30
|
+
light: grey?.[500] || "#9e9e9e",
|
|
31
|
+
contrastText: "#fff"
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
var pick = (...candidates) => candidates.find(Boolean) ?? "";
|
|
35
|
+
var withFallbacks = (theme, palette) => ({
|
|
36
|
+
main: pick(palette?.main, theme.palette.primary.main),
|
|
37
|
+
dark: pick(palette?.dark, palette?.main, theme.palette.primary.dark),
|
|
38
|
+
light: pick(palette?.light, palette?.main, theme.palette.primary.light),
|
|
39
|
+
contrastText: pick(palette?.contrastText, "#fff")
|
|
40
|
+
});
|
|
41
|
+
var getColorFromTheme = (theme, color) => {
|
|
42
|
+
if (color === "neutral") return neutralPalette(theme);
|
|
43
|
+
const colorMap = {
|
|
44
|
+
primary: theme.palette.primary,
|
|
45
|
+
secondary: theme.palette.secondary,
|
|
46
|
+
success: theme.palette.success,
|
|
47
|
+
warning: theme.palette.warning,
|
|
48
|
+
info: theme.palette.info,
|
|
49
|
+
danger: theme.palette.error
|
|
50
|
+
};
|
|
51
|
+
return withFallbacks(theme, colorMap[color] || theme.palette.primary);
|
|
52
|
+
};
|
|
53
|
+
var textareaEmphasisStyles = ({
|
|
54
|
+
theme,
|
|
55
|
+
colorPalette,
|
|
56
|
+
glass,
|
|
57
|
+
gradient,
|
|
58
|
+
glow
|
|
59
|
+
}) => {
|
|
60
|
+
const glassStyles = glass ? {
|
|
61
|
+
backgroundColor: alpha(theme.palette.background.paper, 0.1),
|
|
62
|
+
backdropFilter: "blur(20px)",
|
|
63
|
+
border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,
|
|
64
|
+
"&:hover": {
|
|
65
|
+
backgroundColor: alpha(theme.palette.background.paper, 0.15),
|
|
66
|
+
backdropFilter: "blur(25px)"
|
|
67
|
+
},
|
|
68
|
+
"&:focus": {
|
|
69
|
+
backgroundColor: alpha(theme.palette.background.paper, 0.2),
|
|
70
|
+
backdropFilter: "blur(30px)"
|
|
71
|
+
}
|
|
72
|
+
} : {};
|
|
73
|
+
const gradientStyles = gradient ? {
|
|
74
|
+
background: `linear-gradient(${theme.palette.background.paper}, ${theme.palette.background.paper}) padding-box,
|
|
75
|
+
linear-gradient(135deg, ${colorPalette.main}, ${colorPalette.light}) border-box`,
|
|
76
|
+
border: "2px solid transparent",
|
|
77
|
+
"&:focus": {
|
|
78
|
+
background: `linear-gradient(${theme.palette.background.paper}, ${theme.palette.background.paper}) padding-box,
|
|
79
|
+
linear-gradient(135deg, ${colorPalette.main}, ${colorPalette.dark}) border-box`
|
|
80
|
+
}
|
|
81
|
+
} : {};
|
|
82
|
+
const glowStyles = glow ? {
|
|
83
|
+
animation: `${glowAnimation} 2s ease-in-out infinite`,
|
|
84
|
+
boxShadow: `0 0 10px ${alpha(colorPalette.main, 0.3)}`
|
|
85
|
+
} : {};
|
|
86
|
+
return { ...glassStyles, ...gradientStyles, ...glowStyles };
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
floatAnimation,
|
|
91
|
+
getColorFromTheme,
|
|
92
|
+
textareaEmphasisStyles
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=chunk-PQWPMIGH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/form/Textarea/Textarea.styles.ts"],"sourcesContent":["import { alpha, keyframes } from '@mui/material';\nimport type { CSSObject, Theme } from '@mui/material';\n\nconst glowAnimation = keyframes`\n 0% {\n box-shadow: 0 0 5px currentColor;\n }\n 50% {\n box-shadow: 0 0 20px currentColor, 0 0 30px currentColor;\n }\n 100% {\n box-shadow: 0 0 5px currentColor;\n }\n`;\n\n// Ripple animation for buttons (commented out - not currently used)\n// const rippleAnimation = keyframes`\n// 0% {\n// transform: scale(0);\n// opacity: 1;\n// }\n// 100% {\n// transform: scale(4);\n// opacity: 0;\n// }\n// `;\n\n// Float animation for rich text toolbar\nexport const floatAnimation = keyframes`\n 0% {\n transform: translateY(0px);\n }\n 50% {\n transform: translateY(-3px);\n }\n 100% {\n transform: translateY(0px);\n }\n`;\n\ntype ResolvedPalette = {\n main: string;\n dark: string;\n light: string;\n contrastText: string;\n};\n\ntype PartialPalette = { main: string; dark?: string; light?: string; contrastText?: string };\n\n// grey is indexed by weight rather than main/dark/light, so neutral is built by\n// hand. The literals are the MUI grey values, used if the theme omits a weight.\nconst neutralPalette = (theme: Theme): ResolvedPalette => {\n const grey = theme.palette.grey as unknown as Record<number, string>;\n\n return {\n main: grey?.[700] || '#616161',\n dark: grey?.[800] || '#424242',\n light: grey?.[500] || '#9e9e9e',\n contrastText: '#fff',\n };\n};\n\n// Each slot falls back to the palette's own main, then to primary — a custom\n// theme can define main without dark or light.\nconst pick = (...candidates: Array<string | undefined>): string =>\n candidates.find(Boolean) ?? '';\n\nconst withFallbacks = (theme: Theme, palette: PartialPalette): ResolvedPalette => ({\n main: pick(palette?.main, theme.palette.primary.main),\n dark: pick(palette?.dark, palette?.main, theme.palette.primary.dark),\n light: pick(palette?.light, palette?.main, theme.palette.primary.light),\n contrastText: pick(palette?.contrastText, '#fff'),\n});\n\nexport const getColorFromTheme = (theme: Theme, color: string): ResolvedPalette => {\n if (color === 'neutral') return neutralPalette(theme);\n\n const colorMap: Record<string, PartialPalette> = {\n primary: theme.palette.primary,\n secondary: theme.palette.secondary,\n success: theme.palette.success,\n warning: theme.palette.warning,\n info: theme.palette.info,\n danger: theme.palette.error,\n };\n\n return withFallbacks(theme, colorMap[color] || theme.palette.primary);\n};\n\n// glass, gradient and glow are independent flags that each layer styles onto the\n// base. Spelled out here rather than inline so the styled() callback keeps one\n// branch instead of three.\nexport const textareaEmphasisStyles = ({\n theme,\n colorPalette,\n glass,\n gradient,\n glow,\n}: {\n theme: Theme;\n colorPalette: { main: string; light?: string; dark?: string };\n glass?: boolean;\n gradient?: boolean;\n glow?: boolean;\n}): CSSObject => {\nconst glassStyles = glass\n ? {\n backgroundColor: alpha(theme.palette.background.paper, 0.1),\n backdropFilter: 'blur(20px)',\n border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,\n '&:hover': {\n backgroundColor: alpha(theme.palette.background.paper, 0.15),\n backdropFilter: 'blur(25px)',\n },\n '&:focus': {\n backgroundColor: alpha(theme.palette.background.paper, 0.2),\n backdropFilter: 'blur(30px)',\n },\n }\n : {};\n\n// Gradient border effect\nconst gradientStyles = gradient\n ? {\n background: `linear-gradient(${theme.palette.background.paper}, ${theme.palette.background.paper}) padding-box,\n linear-gradient(135deg, ${colorPalette.main}, ${colorPalette.light}) border-box`,\n border: '2px solid transparent',\n '&:focus': {\n background: `linear-gradient(${theme.palette.background.paper}, ${theme.palette.background.paper}) padding-box,\n linear-gradient(135deg, ${colorPalette.main}, ${colorPalette.dark}) border-box`,\n },\n }\n : {};\n\n// Glow effect\nconst glowStyles = glow\n ? {\n animation: `${glowAnimation} 2s ease-in-out infinite`,\n boxShadow: `0 0 10px ${alpha(colorPalette.main, 0.3)}`,\n }\n : {};\n\n return { ...glassStyles, ...gradientStyles, ...glowStyles };\n};\n"],"mappings":";AAAA,SAAS,OAAO,iBAAiB;AAGjC,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyBf,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuB9B,IAAM,iBAAiB,CAAC,UAAkC;AACxD,QAAM,OAAO,MAAM,QAAQ;AAE3B,SAAO;AAAA,IACL,MAAM,OAAO,GAAG,KAAK;AAAA,IACrB,MAAM,OAAO,GAAG,KAAK;AAAA,IACrB,OAAO,OAAO,GAAG,KAAK;AAAA,IACtB,cAAc;AAAA,EAChB;AACF;AAIA,IAAM,OAAO,IAAI,eACf,WAAW,KAAK,OAAO,KAAK;AAE9B,IAAM,gBAAgB,CAAC,OAAc,aAA8C;AAAA,EACjF,MAAM,KAAK,SAAS,MAAM,MAAM,QAAQ,QAAQ,IAAI;AAAA,EACpD,MAAM,KAAK,SAAS,MAAM,SAAS,MAAM,MAAM,QAAQ,QAAQ,IAAI;AAAA,EACnE,OAAO,KAAK,SAAS,OAAO,SAAS,MAAM,MAAM,QAAQ,QAAQ,KAAK;AAAA,EACtE,cAAc,KAAK,SAAS,cAAc,MAAM;AAClD;AAEO,IAAM,oBAAoB,CAAC,OAAc,UAAmC;AACjF,MAAI,UAAU,UAAW,QAAO,eAAe,KAAK;AAEpD,QAAM,WAA2C;AAAA,IAC/C,SAAS,MAAM,QAAQ;AAAA,IACvB,WAAW,MAAM,QAAQ;AAAA,IACzB,SAAS,MAAM,QAAQ;AAAA,IACvB,SAAS,MAAM,QAAQ;AAAA,IACvB,MAAM,MAAM,QAAQ;AAAA,IACpB,QAAQ,MAAM,QAAQ;AAAA,EACxB;AAEA,SAAO,cAAc,OAAO,SAAS,KAAK,KAAK,MAAM,QAAQ,OAAO;AACtE;AAKO,IAAM,yBAAyB,CAAC;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAMiB;AACjB,QAAM,cAAc,QAChB;AAAA,IACE,iBAAiB,MAAM,MAAM,QAAQ,WAAW,OAAO,GAAG;AAAA,IAC1D,gBAAgB;AAAA,IAChB,QAAQ,aAAa,MAAM,MAAM,QAAQ,SAAS,GAAG,CAAC;AAAA,IACtD,WAAW;AAAA,MACT,iBAAiB,MAAM,MAAM,QAAQ,WAAW,OAAO,IAAI;AAAA,MAC3D,gBAAgB;AAAA,IAClB;AAAA,IACA,WAAW;AAAA,MACT,iBAAiB,MAAM,MAAM,QAAQ,WAAW,OAAO,GAAG;AAAA,MAC1D,gBAAgB;AAAA,IAClB;AAAA,EACF,IACA,CAAC;AAGL,QAAM,iBAAiB,WACnB;AAAA,IACE,YAAY,mBAAmB,MAAM,QAAQ,WAAW,KAAK,KAAK,MAAM,QAAQ,WAAW,KAAK;AAAA,yCAC7D,aAAa,IAAI,KAAK,aAAa,KAAK;AAAA,IAC3E,QAAQ;AAAA,IACR,WAAW;AAAA,MACT,YAAY,mBAAmB,MAAM,QAAQ,WAAW,KAAK,KAAK,MAAM,QAAQ,WAAW,KAAK;AAAA,2CAC7D,aAAa,IAAI,KAAK,aAAa,IAAI;AAAA,IAC5E;AAAA,EACF,IACA,CAAC;AAGL,QAAM,aAAa,OACf;AAAA,IACE,WAAW,GAAG,aAAa;AAAA,IAC3B,WAAW,YAAY,MAAM,aAAa,MAAM,GAAG,CAAC;AAAA,EACtD,IACA,CAAC;AAEH,SAAO,EAAE,GAAG,aAAa,GAAG,gBAAgB,GAAG,WAAW;AAC5D;","names":[]}
|
package/dist/form/Textarea.js
CHANGED
|
@@ -1,401 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getColorFromTheme,
|
|
3
|
+
textareaEmphasisStyles
|
|
4
|
+
} from "../chunk-PQWPMIGH.js";
|
|
1
5
|
import "../chunk-PZ5AY32C.js";
|
|
2
6
|
|
|
3
7
|
// src/components/form/Textarea/Textarea.tsx
|
|
4
8
|
import {
|
|
5
|
-
alpha
|
|
6
|
-
Box
|
|
9
|
+
alpha,
|
|
10
|
+
Box,
|
|
7
11
|
FormHelperText,
|
|
8
12
|
InputLabel,
|
|
9
13
|
TextareaAutosize
|
|
10
14
|
} from "@mui/material";
|
|
11
|
-
import { styled as styled2 } from "@mui/material";
|
|
12
|
-
import { useState as useState2 } from "react";
|
|
13
|
-
|
|
14
|
-
// src/components/form/Textarea/Textarea.styles.ts
|
|
15
|
-
import { alpha, keyframes } from "@mui/material";
|
|
16
|
-
var glowAnimation = keyframes`
|
|
17
|
-
0% {
|
|
18
|
-
box-shadow: 0 0 5px currentColor;
|
|
19
|
-
}
|
|
20
|
-
50% {
|
|
21
|
-
box-shadow: 0 0 20px currentColor, 0 0 30px currentColor;
|
|
22
|
-
}
|
|
23
|
-
100% {
|
|
24
|
-
box-shadow: 0 0 5px currentColor;
|
|
25
|
-
}
|
|
26
|
-
`;
|
|
27
|
-
var floatAnimation = keyframes`
|
|
28
|
-
0% {
|
|
29
|
-
transform: translateY(0px);
|
|
30
|
-
}
|
|
31
|
-
50% {
|
|
32
|
-
transform: translateY(-3px);
|
|
33
|
-
}
|
|
34
|
-
100% {
|
|
35
|
-
transform: translateY(0px);
|
|
36
|
-
}
|
|
37
|
-
`;
|
|
38
|
-
var neutralPalette = (theme) => {
|
|
39
|
-
const grey = theme.palette.grey;
|
|
40
|
-
return {
|
|
41
|
-
main: grey?.[700] || "#616161",
|
|
42
|
-
dark: grey?.[800] || "#424242",
|
|
43
|
-
light: grey?.[500] || "#9e9e9e",
|
|
44
|
-
contrastText: "#fff"
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
var pick = (...candidates) => candidates.find(Boolean) ?? "";
|
|
48
|
-
var withFallbacks = (theme, palette) => ({
|
|
49
|
-
main: pick(palette?.main, theme.palette.primary.main),
|
|
50
|
-
dark: pick(palette?.dark, palette?.main, theme.palette.primary.dark),
|
|
51
|
-
light: pick(palette?.light, palette?.main, theme.palette.primary.light),
|
|
52
|
-
contrastText: pick(palette?.contrastText, "#fff")
|
|
53
|
-
});
|
|
54
|
-
var getColorFromTheme = (theme, color) => {
|
|
55
|
-
if (color === "neutral") return neutralPalette(theme);
|
|
56
|
-
const colorMap = {
|
|
57
|
-
primary: theme.palette.primary,
|
|
58
|
-
secondary: theme.palette.secondary,
|
|
59
|
-
success: theme.palette.success,
|
|
60
|
-
warning: theme.palette.warning,
|
|
61
|
-
info: theme.palette.info,
|
|
62
|
-
danger: theme.palette.error
|
|
63
|
-
};
|
|
64
|
-
return withFallbacks(theme, colorMap[color] || theme.palette.primary);
|
|
65
|
-
};
|
|
66
|
-
var textareaEmphasisStyles = ({
|
|
67
|
-
theme,
|
|
68
|
-
colorPalette,
|
|
69
|
-
glass,
|
|
70
|
-
gradient,
|
|
71
|
-
glow
|
|
72
|
-
}) => {
|
|
73
|
-
const glassStyles = glass ? {
|
|
74
|
-
backgroundColor: alpha(theme.palette.background.paper, 0.1),
|
|
75
|
-
backdropFilter: "blur(20px)",
|
|
76
|
-
border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,
|
|
77
|
-
"&:hover": {
|
|
78
|
-
backgroundColor: alpha(theme.palette.background.paper, 0.15),
|
|
79
|
-
backdropFilter: "blur(25px)"
|
|
80
|
-
},
|
|
81
|
-
"&:focus": {
|
|
82
|
-
backgroundColor: alpha(theme.palette.background.paper, 0.2),
|
|
83
|
-
backdropFilter: "blur(30px)"
|
|
84
|
-
}
|
|
85
|
-
} : {};
|
|
86
|
-
const gradientStyles = gradient ? {
|
|
87
|
-
background: `linear-gradient(${theme.palette.background.paper}, ${theme.palette.background.paper}) padding-box,
|
|
88
|
-
linear-gradient(135deg, ${colorPalette.main}, ${colorPalette.light}) border-box`,
|
|
89
|
-
border: "2px solid transparent",
|
|
90
|
-
"&:focus": {
|
|
91
|
-
background: `linear-gradient(${theme.palette.background.paper}, ${theme.palette.background.paper}) padding-box,
|
|
92
|
-
linear-gradient(135deg, ${colorPalette.main}, ${colorPalette.dark}) border-box`
|
|
93
|
-
}
|
|
94
|
-
} : {};
|
|
95
|
-
const glowStyles = glow ? {
|
|
96
|
-
animation: `${glowAnimation} 2s ease-in-out infinite`,
|
|
97
|
-
boxShadow: `0 0 10px ${alpha(colorPalette.main, 0.3)}`
|
|
98
|
-
} : {};
|
|
99
|
-
return { ...glassStyles, ...gradientStyles, ...glowStyles };
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
// src/components/form/Textarea/TextareaRichEditor.tsx
|
|
103
|
-
import Code from "@mui/icons-material/Code";
|
|
104
|
-
import FormatBold from "@mui/icons-material/FormatBold";
|
|
105
|
-
import FormatColorFill from "@mui/icons-material/FormatColorFill";
|
|
106
|
-
import FormatColorText from "@mui/icons-material/FormatColorText";
|
|
107
|
-
import FormatItalic from "@mui/icons-material/FormatItalic";
|
|
108
|
-
import FormatListBulleted from "@mui/icons-material/FormatListBulleted";
|
|
109
|
-
import FormatListNumbered from "@mui/icons-material/FormatListNumbered";
|
|
110
|
-
import FormatQuote from "@mui/icons-material/FormatQuote";
|
|
111
|
-
import FormatUnderlined from "@mui/icons-material/FormatUnderlined";
|
|
112
|
-
import Link from "@mui/icons-material/Link";
|
|
113
|
-
import { alpha as alpha2, Box, Divider, IconButton, Tooltip } from "@mui/material";
|
|
114
15
|
import { styled } from "@mui/material";
|
|
115
|
-
import {
|
|
116
|
-
import {
|
|
117
|
-
var
|
|
118
|
-
|
|
119
|
-
alignItems: "center",
|
|
120
|
-
gap: theme.spacing(0.5),
|
|
121
|
-
padding: theme.spacing(1),
|
|
122
|
-
borderRadius: `${theme.spacing(1)} ${theme.spacing(1)} 0 0`,
|
|
123
|
-
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
124
|
-
background: glass ? alpha2(theme.palette.background.paper, 0.1) : theme.palette.background.paper,
|
|
125
|
-
...glass && {
|
|
126
|
-
backdropFilter: "blur(15px)"
|
|
127
|
-
},
|
|
128
|
-
"& .MuiDivider-root": {
|
|
129
|
-
height: 24,
|
|
130
|
-
margin: `0 ${theme.spacing(1)}`
|
|
131
|
-
}
|
|
132
|
-
}));
|
|
133
|
-
var ToolbarButton = styled(IconButton, {
|
|
134
|
-
shouldForwardProp: (prop) => prop !== "active"
|
|
135
|
-
})(({ theme, active }) => ({
|
|
136
|
-
padding: theme.spacing(0.75),
|
|
137
|
-
borderRadius: theme.spacing(0.5),
|
|
138
|
-
color: active ? theme.palette.primary.main : theme.palette.text.secondary,
|
|
139
|
-
backgroundColor: active ? alpha2(theme.palette.primary.main, 0.1) : "transparent",
|
|
140
|
-
transition: "all 0.2s ease",
|
|
141
|
-
position: "relative",
|
|
142
|
-
overflow: "hidden",
|
|
143
|
-
"&::before": {
|
|
144
|
-
content: '""',
|
|
145
|
-
position: "absolute",
|
|
146
|
-
top: "50%",
|
|
147
|
-
left: "50%",
|
|
148
|
-
width: 0,
|
|
149
|
-
height: 0,
|
|
150
|
-
borderRadius: "50%",
|
|
151
|
-
backgroundColor: alpha2(theme.palette.primary.main, 0.3),
|
|
152
|
-
transform: "translate(-50%, -50%)",
|
|
153
|
-
transition: "width 0.3s, height 0.3s"
|
|
154
|
-
},
|
|
155
|
-
"&:hover": {
|
|
156
|
-
color: theme.palette.primary.main,
|
|
157
|
-
backgroundColor: alpha2(theme.palette.primary.main, 0.15),
|
|
158
|
-
transform: "translateY(-1px)",
|
|
159
|
-
animation: `${floatAnimation} 1.5s ease-in-out infinite`,
|
|
160
|
-
"&::before": {
|
|
161
|
-
width: "100%",
|
|
162
|
-
height: "100%"
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
"&:active": {
|
|
166
|
-
transform: "scale(0.95)"
|
|
167
|
-
}
|
|
16
|
+
import { lazy, Suspense, useState } from "react";
|
|
17
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
18
|
+
var TextareaRichEditor = lazy(async () => ({
|
|
19
|
+
default: (await import("../TextareaRichEditor-ZFB44VWZ.js")).TextareaRichEditor
|
|
168
20
|
}));
|
|
169
|
-
var
|
|
170
|
-
const isWarning = limit && count > limit * 0.8;
|
|
171
|
-
const isError = limit && count > limit;
|
|
172
|
-
return {
|
|
173
|
-
position: "absolute",
|
|
174
|
-
bottom: theme.spacing(1),
|
|
175
|
-
right: theme.spacing(1),
|
|
176
|
-
fontSize: "0.75rem",
|
|
177
|
-
color: isError ? theme.palette.error.main : isWarning ? theme.palette.warning.main : theme.palette.text.secondary,
|
|
178
|
-
padding: "2px 6px",
|
|
179
|
-
borderRadius: theme.spacing(0.5),
|
|
180
|
-
backgroundColor: alpha2(theme.palette.background.paper, 0.8),
|
|
181
|
-
backdropFilter: "blur(10px)",
|
|
182
|
-
transition: "all 0.3s ease"
|
|
183
|
-
};
|
|
184
|
-
});
|
|
185
|
-
var ContentEditableDiv = styled("div")(({ theme, error, focused, glass, customColor = "primary" }) => {
|
|
186
|
-
if (!theme) return {};
|
|
187
|
-
const colorPalette = getColorFromTheme(theme, customColor);
|
|
188
|
-
const errorColor = theme.palette.error;
|
|
189
|
-
return {
|
|
190
|
-
minHeight: "120px",
|
|
191
|
-
padding: theme.spacing(1.5),
|
|
192
|
-
borderRadius: `0 0 ${theme.spacing(1)} ${theme.spacing(1)}`,
|
|
193
|
-
border: `2px solid ${error ? errorColor.main : focused ? colorPalette.main : theme.palette.divider}`,
|
|
194
|
-
borderTop: "none",
|
|
195
|
-
backgroundColor: glass ? alpha2(theme.palette.background.paper, 0.1) : theme.palette.background.paper,
|
|
196
|
-
color: theme.palette.text.primary,
|
|
197
|
-
fontFamily: theme.typography.fontFamily,
|
|
198
|
-
fontSize: "1rem",
|
|
199
|
-
lineHeight: 1.5,
|
|
200
|
-
outline: "none",
|
|
201
|
-
transition: "all 0.3s ease",
|
|
202
|
-
cursor: "text",
|
|
203
|
-
...glass && {
|
|
204
|
-
backdropFilter: "blur(20px)"
|
|
205
|
-
},
|
|
206
|
-
"&:hover": {
|
|
207
|
-
backgroundColor: glass ? alpha2(theme.palette.background.paper, 0.15) : alpha2(theme.palette.background.paper, 0.9)
|
|
208
|
-
},
|
|
209
|
-
"&:focus": {
|
|
210
|
-
borderColor: error ? errorColor.main : colorPalette.main,
|
|
211
|
-
boxShadow: `0 0 0 3px ${alpha2(error ? errorColor.main : colorPalette.main, 0.1)}`
|
|
212
|
-
},
|
|
213
|
-
"& > *": {
|
|
214
|
-
margin: "0.5em 0"
|
|
215
|
-
},
|
|
216
|
-
'&[contenteditable="true"]:empty::before': {
|
|
217
|
-
content: "attr(data-placeholder)",
|
|
218
|
-
color: theme.palette.text.secondary,
|
|
219
|
-
opacity: 0.6
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
});
|
|
223
|
-
var formatText = (command, value) => {
|
|
224
|
-
document.execCommand(command, false, value);
|
|
225
|
-
};
|
|
226
|
-
var useRichEditorState = ({
|
|
227
|
-
value,
|
|
228
|
-
onChange
|
|
229
|
-
}) => {
|
|
230
|
-
const [focused, setFocused] = useState(false);
|
|
231
|
-
const [characterCount, setCharacterCount] = useState(0);
|
|
232
|
-
const [activeFormats, setActiveFormats] = useState({
|
|
233
|
-
bold: false,
|
|
234
|
-
italic: false,
|
|
235
|
-
underline: false,
|
|
236
|
-
list: false,
|
|
237
|
-
orderedList: false,
|
|
238
|
-
quote: false,
|
|
239
|
-
code: false
|
|
240
|
-
});
|
|
241
|
-
const contentRef = useRef(null);
|
|
242
|
-
useEffect(() => {
|
|
243
|
-
if (contentRef.current && value !== void 0) {
|
|
244
|
-
if (contentRef.current.innerHTML !== value) {
|
|
245
|
-
contentRef.current.innerHTML = value;
|
|
246
|
-
}
|
|
247
|
-
setCharacterCount(contentRef.current.innerText.length);
|
|
248
|
-
}
|
|
249
|
-
}, [value]);
|
|
250
|
-
const handleInput = () => {
|
|
251
|
-
if (contentRef.current) {
|
|
252
|
-
const html = contentRef.current.innerHTML;
|
|
253
|
-
onChange?.(html);
|
|
254
|
-
setCharacterCount(contentRef.current.innerText.length);
|
|
255
|
-
updateActiveFormats();
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
const updateActiveFormats = () => {
|
|
259
|
-
setActiveFormats({
|
|
260
|
-
bold: document.queryCommandState("bold"),
|
|
261
|
-
italic: document.queryCommandState("italic"),
|
|
262
|
-
underline: document.queryCommandState("underline"),
|
|
263
|
-
list: document.queryCommandState("insertUnorderedList"),
|
|
264
|
-
orderedList: document.queryCommandState("insertOrderedList"),
|
|
265
|
-
quote: false,
|
|
266
|
-
code: false
|
|
267
|
-
});
|
|
268
|
-
};
|
|
269
|
-
const handleFormat = (command, value2) => {
|
|
270
|
-
formatText(command, value2);
|
|
271
|
-
updateActiveFormats();
|
|
272
|
-
contentRef.current?.focus();
|
|
273
|
-
};
|
|
274
|
-
return {
|
|
275
|
-
focused,
|
|
276
|
-
setFocused,
|
|
277
|
-
characterCount,
|
|
278
|
-
activeFormats,
|
|
279
|
-
contentRef,
|
|
280
|
-
handleInput,
|
|
281
|
-
handleFormat,
|
|
282
|
-
updateActiveFormats
|
|
283
|
-
};
|
|
284
|
-
};
|
|
285
|
-
var InlineFormatButtons = ({ copy, activeFormats, onCommand }) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
286
|
-
/* @__PURE__ */ jsx(Tooltip, { title: copy.bold, arrow: true, children: /* @__PURE__ */ jsx(
|
|
287
|
-
ToolbarButton,
|
|
288
|
-
{
|
|
289
|
-
size: "small",
|
|
290
|
-
active: activeFormats.bold,
|
|
291
|
-
onClick: () => onCommand("bold"),
|
|
292
|
-
children: /* @__PURE__ */ jsx(FormatBold, { fontSize: "small" })
|
|
293
|
-
}
|
|
294
|
-
) }),
|
|
295
|
-
/* @__PURE__ */ jsx(Tooltip, { title: copy.italic, arrow: true, children: /* @__PURE__ */ jsx(
|
|
296
|
-
ToolbarButton,
|
|
297
|
-
{
|
|
298
|
-
size: "small",
|
|
299
|
-
active: activeFormats.italic,
|
|
300
|
-
onClick: () => onCommand("italic"),
|
|
301
|
-
children: /* @__PURE__ */ jsx(FormatItalic, { fontSize: "small" })
|
|
302
|
-
}
|
|
303
|
-
) }),
|
|
304
|
-
/* @__PURE__ */ jsx(Tooltip, { title: copy.underline, arrow: true, children: /* @__PURE__ */ jsx(
|
|
305
|
-
ToolbarButton,
|
|
306
|
-
{
|
|
307
|
-
size: "small",
|
|
308
|
-
active: activeFormats.underline,
|
|
309
|
-
onClick: () => onCommand("underline"),
|
|
310
|
-
children: /* @__PURE__ */ jsx(FormatUnderlined, { fontSize: "small" })
|
|
311
|
-
}
|
|
312
|
-
) })
|
|
313
|
-
] });
|
|
314
|
-
var BlockFormatButtons = ({ copy, activeFormats, onCommand }) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
315
|
-
/* @__PURE__ */ jsx(Divider, { orientation: "vertical", flexItem: true }),
|
|
316
|
-
/* @__PURE__ */ jsx(Tooltip, { title: copy.bulletList, arrow: true, children: /* @__PURE__ */ jsx(
|
|
317
|
-
ToolbarButton,
|
|
318
|
-
{
|
|
319
|
-
size: "small",
|
|
320
|
-
active: activeFormats.list,
|
|
321
|
-
onClick: () => onCommand("insertUnorderedList"),
|
|
322
|
-
children: /* @__PURE__ */ jsx(FormatListBulleted, { fontSize: "small" })
|
|
323
|
-
}
|
|
324
|
-
) }),
|
|
325
|
-
/* @__PURE__ */ jsx(Tooltip, { title: copy.numberedList, arrow: true, children: /* @__PURE__ */ jsx(
|
|
326
|
-
ToolbarButton,
|
|
327
|
-
{
|
|
328
|
-
size: "small",
|
|
329
|
-
active: activeFormats.orderedList,
|
|
330
|
-
onClick: () => onCommand("insertOrderedList"),
|
|
331
|
-
children: /* @__PURE__ */ jsx(FormatListNumbered, { fontSize: "small" })
|
|
332
|
-
}
|
|
333
|
-
) }),
|
|
334
|
-
/* @__PURE__ */ jsx(Divider, { orientation: "vertical", flexItem: true }),
|
|
335
|
-
/* @__PURE__ */ jsx(Tooltip, { title: copy.quote, arrow: true, children: /* @__PURE__ */ jsx(ToolbarButton, { size: "small", onClick: () => onCommand("formatBlock", "blockquote"), children: /* @__PURE__ */ jsx(FormatQuote, { fontSize: "small" }) }) }),
|
|
336
|
-
/* @__PURE__ */ jsx(Tooltip, { title: copy.code, arrow: true, children: /* @__PURE__ */ jsx(ToolbarButton, { size: "small", onClick: () => onCommand("formatBlock", "pre"), children: /* @__PURE__ */ jsx(Code, { fontSize: "small" }) }) }),
|
|
337
|
-
/* @__PURE__ */ jsx(Divider, { orientation: "vertical", flexItem: true }),
|
|
338
|
-
/* @__PURE__ */ jsx(Tooltip, { title: copy.insertLink, arrow: true, children: /* @__PURE__ */ jsx(
|
|
339
|
-
ToolbarButton,
|
|
340
|
-
{
|
|
341
|
-
size: "small",
|
|
342
|
-
onClick: () => {
|
|
343
|
-
const url = window.prompt(copy.linkPrompt);
|
|
344
|
-
if (url) onCommand("createLink", url);
|
|
345
|
-
},
|
|
346
|
-
children: /* @__PURE__ */ jsx(Link, { fontSize: "small" })
|
|
347
|
-
}
|
|
348
|
-
) }),
|
|
349
|
-
/* @__PURE__ */ jsx(Tooltip, { title: copy.textColor, arrow: true, children: /* @__PURE__ */ jsx(ToolbarButton, { size: "small", children: /* @__PURE__ */ jsx(FormatColorText, { fontSize: "small" }) }) }),
|
|
350
|
-
/* @__PURE__ */ jsx(Tooltip, { title: copy.backgroundColor, arrow: true, children: /* @__PURE__ */ jsx(ToolbarButton, { size: "small", children: /* @__PURE__ */ jsx(FormatColorFill, { fontSize: "small" }) }) })
|
|
351
|
-
] });
|
|
352
|
-
var EditorToolbar = ({ copy, glass, activeFormats, onCommand }) => /* @__PURE__ */ jsxs(RichToolbar, { glass, children: [
|
|
353
|
-
/* @__PURE__ */ jsx(InlineFormatButtons, { copy, activeFormats, onCommand }),
|
|
354
|
-
/* @__PURE__ */ jsx(BlockFormatButtons, { copy, activeFormats, onCommand })
|
|
355
|
-
] });
|
|
356
|
-
var TextareaRichEditor = ({ copy, value, onChange, placeholder, error, glass, color, characterLimit }) => {
|
|
357
|
-
const {
|
|
358
|
-
focused,
|
|
359
|
-
setFocused,
|
|
360
|
-
characterCount,
|
|
361
|
-
activeFormats,
|
|
362
|
-
contentRef,
|
|
363
|
-
handleInput,
|
|
364
|
-
handleFormat,
|
|
365
|
-
updateActiveFormats
|
|
366
|
-
} = useRichEditorState({ value, onChange });
|
|
367
|
-
return /* @__PURE__ */ jsxs(Box, { sx: { position: "relative" }, children: [
|
|
368
|
-
/* @__PURE__ */ jsx(EditorToolbar, { copy, glass, activeFormats, onCommand: handleFormat }),
|
|
369
|
-
/* @__PURE__ */ jsx(
|
|
370
|
-
ContentEditableDiv,
|
|
371
|
-
{
|
|
372
|
-
ref: contentRef,
|
|
373
|
-
contentEditable: true,
|
|
374
|
-
"data-placeholder": placeholder,
|
|
375
|
-
error,
|
|
376
|
-
focused,
|
|
377
|
-
glass,
|
|
378
|
-
customColor: color,
|
|
379
|
-
onFocus: () => setFocused(true),
|
|
380
|
-
onBlur: () => setFocused(false),
|
|
381
|
-
onInput: handleInput,
|
|
382
|
-
onKeyUp: updateActiveFormats,
|
|
383
|
-
onMouseUp: updateActiveFormats,
|
|
384
|
-
role: "textbox",
|
|
385
|
-
"aria-multiline": "true",
|
|
386
|
-
"aria-label": placeholder || "Rich text editor"
|
|
387
|
-
}
|
|
388
|
-
),
|
|
389
|
-
characterLimit && /* @__PURE__ */ jsxs(CharacterCount, { limit: characterLimit, count: characterCount, children: [
|
|
390
|
-
characterCount,
|
|
391
|
-
characterLimit ? `/${characterLimit}` : ""
|
|
392
|
-
] })
|
|
393
|
-
] });
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
// src/components/form/Textarea/Textarea.tsx
|
|
397
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
398
|
-
var StyledTextarea = styled2(TextareaAutosize, {
|
|
21
|
+
var StyledTextarea = styled(TextareaAutosize, {
|
|
399
22
|
shouldForwardProp: (prop) => !["customVariant", "customColor", "customSize", "glow", "glass", "gradient", "error"].includes(
|
|
400
23
|
prop
|
|
401
24
|
)
|
|
@@ -436,12 +59,12 @@ var StyledTextarea = styled2(TextareaAutosize, {
|
|
|
436
59
|
},
|
|
437
60
|
"&:hover": {
|
|
438
61
|
borderColor: error ? errorColor.dark : colorPalette.main,
|
|
439
|
-
backgroundColor:
|
|
62
|
+
backgroundColor: alpha(theme.palette.background.paper, 0.8)
|
|
440
63
|
},
|
|
441
64
|
"&:focus": {
|
|
442
65
|
borderColor: error ? errorColor.main : colorPalette.main,
|
|
443
66
|
backgroundColor: theme.palette.background.paper,
|
|
444
|
-
boxShadow: `0 0 0 3px ${
|
|
67
|
+
boxShadow: `0 0 0 3px ${alpha(error ? errorColor.main : colorPalette.main, 0.1)}`
|
|
445
68
|
}
|
|
446
69
|
};
|
|
447
70
|
return {
|
|
@@ -449,18 +72,18 @@ var StyledTextarea = styled2(TextareaAutosize, {
|
|
|
449
72
|
...textareaEmphasisStyles({ theme, colorPalette, glass, gradient, glow })
|
|
450
73
|
};
|
|
451
74
|
});
|
|
452
|
-
var StyledLabel =
|
|
75
|
+
var StyledLabel = styled(InputLabel, {
|
|
453
76
|
shouldForwardProp: (prop) => !["glass", "error"].includes(prop)
|
|
454
77
|
})(({ theme, glass, error }) => ({
|
|
455
78
|
marginBottom: theme.spacing(1),
|
|
456
79
|
fontWeight: 500,
|
|
457
80
|
color: error ? theme.palette.error.main : theme.palette.text.primary,
|
|
458
81
|
...glass && {
|
|
459
|
-
backgroundColor:
|
|
82
|
+
backgroundColor: alpha(theme.palette.background.paper, 0.1),
|
|
460
83
|
backdropFilter: "blur(10px)",
|
|
461
84
|
padding: "4px 8px",
|
|
462
85
|
borderRadius: "4px",
|
|
463
|
-
border: `1px solid ${
|
|
86
|
+
border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,
|
|
464
87
|
display: "inline-block"
|
|
465
88
|
}
|
|
466
89
|
}));
|
|
@@ -480,20 +103,20 @@ var TEXTAREA_DEFAULTS = {
|
|
|
480
103
|
var definedProps = (props) => Object.fromEntries(
|
|
481
104
|
Object.entries(props).filter(([, value]) => value !== void 0)
|
|
482
105
|
);
|
|
483
|
-
var TextareaShell = ({ testId, label, glassLabel, error, helperText, children }) => /* @__PURE__ */
|
|
484
|
-
|
|
106
|
+
var TextareaShell = ({ testId, label, glassLabel, error, helperText, children }) => /* @__PURE__ */ jsxs(
|
|
107
|
+
Box,
|
|
485
108
|
{
|
|
486
109
|
sx: { position: "relative", width: "100%" },
|
|
487
110
|
"data-testid": testId ? `${testId}-wrapper` : void 0,
|
|
488
111
|
children: [
|
|
489
|
-
label && /* @__PURE__ */
|
|
112
|
+
label && /* @__PURE__ */ jsx(StyledLabel, { glass: glassLabel, error, children: label }),
|
|
490
113
|
children,
|
|
491
|
-
helperText && /* @__PURE__ */
|
|
114
|
+
helperText && /* @__PURE__ */ jsx(FormHelperText, { error, sx: { mt: 1 }, children: helperText })
|
|
492
115
|
]
|
|
493
116
|
}
|
|
494
117
|
);
|
|
495
|
-
var TextareaIcon = ({ icon, iconPosition }) => /* @__PURE__ */
|
|
496
|
-
|
|
118
|
+
var TextareaIcon = ({ icon, iconPosition }) => /* @__PURE__ */ jsx(
|
|
119
|
+
Box,
|
|
497
120
|
{
|
|
498
121
|
sx: {
|
|
499
122
|
position: "absolute",
|
|
@@ -506,6 +129,12 @@ var TextareaIcon = ({ icon, iconPosition }) => /* @__PURE__ */ jsx2(
|
|
|
506
129
|
children: icon
|
|
507
130
|
}
|
|
508
131
|
);
|
|
132
|
+
function RichVariant({
|
|
133
|
+
shell,
|
|
134
|
+
editor
|
|
135
|
+
}) {
|
|
136
|
+
return /* @__PURE__ */ jsx(TextareaShell, { ...shell, children: /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(TextareaRichEditor, { ...editor }) }) });
|
|
137
|
+
}
|
|
509
138
|
var Textarea = (textareaProps) => {
|
|
510
139
|
const {
|
|
511
140
|
variant,
|
|
@@ -528,7 +157,7 @@ var Textarea = (textareaProps) => {
|
|
|
528
157
|
...props
|
|
529
158
|
} = { ...TEXTAREA_DEFAULTS, ...definedProps(textareaProps) };
|
|
530
159
|
const { richEditorCopy } = textareaProps;
|
|
531
|
-
const [richTextValue, setRichTextValue] =
|
|
160
|
+
const [richTextValue, setRichTextValue] = useState("");
|
|
532
161
|
const hasIcon = Boolean(icon);
|
|
533
162
|
const testId = dataTestId || dataTestIdCamelCase;
|
|
534
163
|
const textareaStyle = {
|
|
@@ -541,23 +170,26 @@ var Textarea = (textareaProps) => {
|
|
|
541
170
|
}
|
|
542
171
|
};
|
|
543
172
|
if (variant === "rich" && richEditorCopy) {
|
|
544
|
-
return /* @__PURE__ */
|
|
545
|
-
|
|
173
|
+
return /* @__PURE__ */ jsx(
|
|
174
|
+
RichVariant,
|
|
546
175
|
{
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
176
|
+
shell: { testId, label, glassLabel, error, helperText },
|
|
177
|
+
editor: {
|
|
178
|
+
copy: richEditorCopy,
|
|
179
|
+
value: richTextValue,
|
|
180
|
+
onChange: setRichTextValue,
|
|
181
|
+
placeholder: props.placeholder,
|
|
182
|
+
error,
|
|
183
|
+
glass,
|
|
184
|
+
color,
|
|
185
|
+
characterLimit: props.maxLength
|
|
186
|
+
}
|
|
555
187
|
}
|
|
556
|
-
)
|
|
188
|
+
);
|
|
557
189
|
}
|
|
558
|
-
return /* @__PURE__ */
|
|
559
|
-
hasIcon && /* @__PURE__ */
|
|
560
|
-
/* @__PURE__ */
|
|
190
|
+
return /* @__PURE__ */ jsx(TextareaShell, { testId, label, glassLabel, error, helperText, children: /* @__PURE__ */ jsxs(Box, { sx: { position: "relative" }, children: [
|
|
191
|
+
hasIcon && /* @__PURE__ */ jsx(TextareaIcon, { icon, iconPosition }),
|
|
192
|
+
/* @__PURE__ */ jsx(
|
|
561
193
|
StyledTextarea,
|
|
562
194
|
{
|
|
563
195
|
customVariant: variant,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/form/Textarea/Textarea.tsx","../../src/components/form/Textarea/Textarea.styles.ts","../../src/components/form/Textarea/TextareaRichEditor.tsx"],"sourcesContent":["import {\n alpha,\n Box,\n FormHelperText,\n InputLabel,\n TextareaAutosize } from '@mui/material';\nimport type { Theme } from '@mui/material';\nimport { styled } from '@mui/material';\nimport React, { useState } from 'react';\n\nimport {\n getColorFromTheme,\n textareaEmphasisStyles } from './Textarea.styles';\nimport { TextareaRichEditor } from './TextareaRichEditor';\nimport type { TextareaProps } from './Textarea.types';\n\n// Interface for styled component props\ninterface StyledTextareaProps {\n customVariant?: string;\n customColor?: string;\n customSize?: string;\n glow?: boolean;\n glass?: boolean;\n gradient?: boolean;\n error?: boolean;\n theme?: Theme;\n}\n\n// Glow animation for enhanced visual effects\nconst StyledTextarea = styled(TextareaAutosize, {\n shouldForwardProp: (prop) =>\n !['customVariant', 'customColor', 'customSize', 'glow', 'glass', 'gradient', 'error'].includes(\n prop as string,\n ) })<StyledTextareaProps>(({\n theme,\n customVariant,\n customColor = 'primary',\n customSize = 'md',\n glow,\n glass,\n gradient,\n error }) => {\n if (!theme) return {};\n const colorPalette = getColorFromTheme(theme, customColor);\n const errorColor = theme.palette.error;\n\n const sizeMap = {\n xs: { padding: '6px 8px', fontSize: '0.75rem', minHeight: '60px' },\n sm: { padding: '8px 10px', fontSize: '0.875rem', minHeight: '80px' },\n md: { padding: '10px 12px', fontSize: '1rem', minHeight: '100px' },\n lg: { padding: '12px 14px', fontSize: '1.125rem', minHeight: '120px' },\n xl: { padding: '14px 16px', fontSize: '1.25rem', minHeight: '140px' } };\n\n const baseStyles = {\n width: '100%',\n fontFamily: theme.typography.fontFamily,\n borderRadius: theme.spacing(1),\n border: `2px solid ${error ? errorColor.main : theme.palette.divider}`,\n backgroundColor: theme.palette.background.paper,\n color: theme.palette.text.primary,\n transition: 'all 0.3s ease',\n resize: customVariant === 'resizable' ? 'vertical' : 'none',\n outline: 'none',\n ...sizeMap[customSize as keyof typeof sizeMap],\n\n '&::placeholder': {\n color: theme.palette.text.secondary,\n opacity: 0.7 },\n\n '&:hover': {\n borderColor: error ? errorColor.dark : colorPalette.main,\n backgroundColor: alpha(theme.palette.background.paper, 0.8) },\n\n '&:focus': {\n borderColor: error ? errorColor.main : colorPalette.main,\n backgroundColor: theme.palette.background.paper,\n boxShadow: `0 0 0 3px ${alpha(error ? errorColor.main : colorPalette.main, 0.1)}` } };\n\n // Glass morphism effect\n return {\n ...baseStyles,\n ...textareaEmphasisStyles({ theme, colorPalette, glass, gradient, glow }) };\n});\n\nconst StyledLabel = styled(InputLabel, {\n shouldForwardProp: (prop) => !['glass', 'error'].includes(prop as string) })<{ glass?: boolean; error?: boolean }>(({ theme, glass, error }) => ({\n marginBottom: theme.spacing(1),\n fontWeight: 500,\n color: error ? theme.palette.error.main : theme.palette.text.primary,\n ...(glass && {\n backgroundColor: alpha(theme.palette.background.paper, 0.1),\n backdropFilter: 'blur(10px)',\n padding: '4px 8px',\n borderRadius: '4px',\n border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,\n display: 'inline-block' }) }));\n\n// IconWrapper - replaced with inline Box component for better TypeScript compatibility\n// const IconWrapper = styled(Box)<{ position: 'start' | 'end' }>(({ theme, position }) => {\n// const positionStyles = position === 'start' ? { left: '12px' } : { right: '12px' };\n// return {\n// position: 'absolute' as const,\n// top: '12px',\n// ...positionStyles,\n// color: theme.palette.text.secondary,\n// pointerEvents: 'none' as const,\n// zIndex: 1,\n// };\n// });\n\n// Rich text toolbar styling\n\nconst ICON_GUTTER = '40px';\n\nconst TEXTAREA_DEFAULTS = {\n variant: 'default',\n color: 'primary',\n size: 'md',\n error: false,\n glassLabel: false,\n glow: false,\n glass: false,\n gradient: false,\n iconPosition: 'start',\n minRows: 3 } satisfies Partial<TextareaProps>;\n\n// Strips explicitly-undefined props before the merge, so `prop={undefined}`\n// still falls back to the default as a destructuring default would.\nconst definedProps = (props: TextareaProps): Partial<TextareaProps> =>\n Object.fromEntries(\n Object.entries(props).filter(([, value]) => value !== undefined),\n ) as Partial<TextareaProps>;\n\n// Label above, helper text below — identical for the plain and rich variants,\n// which previously duplicated both.\nconst TextareaShell: React.FC<{\n testId?: string;\n label?: React.ReactNode;\n glassLabel: boolean;\n error: boolean;\n helperText?: React.ReactNode;\n children: React.ReactNode;\n}> = ({ testId, label, glassLabel, error, helperText, children }) => (\n <Box\n sx={{ position: 'relative', width: '100%' }}\n data-testid={testId ? `${testId}-wrapper` : undefined}\n >\n {label && (\n <StyledLabel glass={glassLabel} error={error}>\n {label}\n </StyledLabel>\n )}\n\n {children}\n\n {helperText && (\n <FormHelperText error={error} sx={{ mt: 1 }}>\n {helperText}\n </FormHelperText>\n )}\n </Box>\n);\n\nconst TextareaIcon: React.FC<{\n icon?: React.ReactNode;\n iconPosition: 'start' | 'end';\n}> = ({ icon, iconPosition }) => (\n <Box\n sx={{\n position: 'absolute',\n top: '12px',\n ...(iconPosition === 'start' ? { left: '12px' } : { right: '12px' }),\n color: 'text.secondary',\n pointerEvents: 'none',\n zIndex: 1 }}\n >\n {icon}\n </Box>\n);\n\nexport const Textarea: React.FC<TextareaProps> = (textareaProps) => {\n const {\n variant,\n color,\n size,\n error,\n helperText,\n label,\n glassLabel,\n glow,\n glass,\n gradient,\n icon,\n iconPosition,\n minRows,\n maxRows,\n style,\n 'data-testid': dataTestId,\n dataTestId: dataTestIdCamelCase,\n ...props\n } = { ...TEXTAREA_DEFAULTS, ...definedProps(textareaProps) };\n\n // Read off the props rather than the merged object: the spread widens every\n // key to optional, which would lose what the variant union already proved.\n const { richEditorCopy } = textareaProps;\n\n const [richTextValue, setRichTextValue] = useState('');\n const hasIcon = Boolean(icon);\n const testId = dataTestId || dataTestIdCamelCase;\n\n const textareaStyle = {\n ...style,\n // Same gap either side; only which side it applies to changes. The previous\n // version had `iconPosition === 'start' ? '40px' : '40px'`, a ternary whose\n // two branches were identical.\n ...(hasIcon && {\n [iconPosition === 'start' ? 'paddingLeft' : 'paddingRight']: ICON_GUTTER }) };\n\n // If rich text variant, use the rich text editor\n if (variant === 'rich' && richEditorCopy) {\n return (\n <TextareaShell testId={testId} label={label} glassLabel={glassLabel} error={error} helperText={helperText}>\n <TextareaRichEditor\n copy={richEditorCopy}\n value={richTextValue}\n onChange={setRichTextValue}\n placeholder={props.placeholder}\n error={error}\n glass={glass}\n color={color}\n characterLimit={props.maxLength}\n />\n </TextareaShell>\n );\n }\n\n return (\n <TextareaShell testId={testId} label={label} glassLabel={glassLabel} error={error} helperText={helperText}>\n <Box sx={{ position: 'relative' }}>\n {hasIcon && <TextareaIcon icon={icon} iconPosition={iconPosition} />}\n\n <StyledTextarea\n customVariant={variant}\n customColor={color}\n customSize={size}\n glow={glow}\n glass={glass}\n gradient={gradient}\n error={error}\n minRows={variant === 'autosize' ? minRows : undefined}\n maxRows={variant === 'autosize' ? maxRows : undefined}\n style={textareaStyle}\n aria-label={label || props['aria-label']}\n data-testid={testId}\n {...props}\n />\n </Box>\n </TextareaShell>\n );\n};\n\nTextarea.displayName = 'Textarea';\n","import { alpha, keyframes } from '@mui/material';\nimport type { CSSObject, Theme } from '@mui/material';\n\nconst glowAnimation = keyframes`\n 0% {\n box-shadow: 0 0 5px currentColor;\n }\n 50% {\n box-shadow: 0 0 20px currentColor, 0 0 30px currentColor;\n }\n 100% {\n box-shadow: 0 0 5px currentColor;\n }\n`;\n\n// Ripple animation for buttons (commented out - not currently used)\n// const rippleAnimation = keyframes`\n// 0% {\n// transform: scale(0);\n// opacity: 1;\n// }\n// 100% {\n// transform: scale(4);\n// opacity: 0;\n// }\n// `;\n\n// Float animation for rich text toolbar\nexport const floatAnimation = keyframes`\n 0% {\n transform: translateY(0px);\n }\n 50% {\n transform: translateY(-3px);\n }\n 100% {\n transform: translateY(0px);\n }\n`;\n\ntype ResolvedPalette = {\n main: string;\n dark: string;\n light: string;\n contrastText: string;\n};\n\ntype PartialPalette = { main: string; dark?: string; light?: string; contrastText?: string };\n\n// grey is indexed by weight rather than main/dark/light, so neutral is built by\n// hand. The literals are the MUI grey values, used if the theme omits a weight.\nconst neutralPalette = (theme: Theme): ResolvedPalette => {\n const grey = theme.palette.grey as unknown as Record<number, string>;\n\n return {\n main: grey?.[700] || '#616161',\n dark: grey?.[800] || '#424242',\n light: grey?.[500] || '#9e9e9e',\n contrastText: '#fff',\n };\n};\n\n// Each slot falls back to the palette's own main, then to primary — a custom\n// theme can define main without dark or light.\nconst pick = (...candidates: Array<string | undefined>): string =>\n candidates.find(Boolean) ?? '';\n\nconst withFallbacks = (theme: Theme, palette: PartialPalette): ResolvedPalette => ({\n main: pick(palette?.main, theme.palette.primary.main),\n dark: pick(palette?.dark, palette?.main, theme.palette.primary.dark),\n light: pick(palette?.light, palette?.main, theme.palette.primary.light),\n contrastText: pick(palette?.contrastText, '#fff'),\n});\n\nexport const getColorFromTheme = (theme: Theme, color: string): ResolvedPalette => {\n if (color === 'neutral') return neutralPalette(theme);\n\n const colorMap: Record<string, PartialPalette> = {\n primary: theme.palette.primary,\n secondary: theme.palette.secondary,\n success: theme.palette.success,\n warning: theme.palette.warning,\n info: theme.palette.info,\n danger: theme.palette.error,\n };\n\n return withFallbacks(theme, colorMap[color] || theme.palette.primary);\n};\n\n// glass, gradient and glow are independent flags that each layer styles onto the\n// base. Spelled out here rather than inline so the styled() callback keeps one\n// branch instead of three.\nexport const textareaEmphasisStyles = ({\n theme,\n colorPalette,\n glass,\n gradient,\n glow,\n}: {\n theme: Theme;\n colorPalette: { main: string; light?: string; dark?: string };\n glass?: boolean;\n gradient?: boolean;\n glow?: boolean;\n}): CSSObject => {\nconst glassStyles = glass\n ? {\n backgroundColor: alpha(theme.palette.background.paper, 0.1),\n backdropFilter: 'blur(20px)',\n border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,\n '&:hover': {\n backgroundColor: alpha(theme.palette.background.paper, 0.15),\n backdropFilter: 'blur(25px)',\n },\n '&:focus': {\n backgroundColor: alpha(theme.palette.background.paper, 0.2),\n backdropFilter: 'blur(30px)',\n },\n }\n : {};\n\n// Gradient border effect\nconst gradientStyles = gradient\n ? {\n background: `linear-gradient(${theme.palette.background.paper}, ${theme.palette.background.paper}) padding-box,\n linear-gradient(135deg, ${colorPalette.main}, ${colorPalette.light}) border-box`,\n border: '2px solid transparent',\n '&:focus': {\n background: `linear-gradient(${theme.palette.background.paper}, ${theme.palette.background.paper}) padding-box,\n linear-gradient(135deg, ${colorPalette.main}, ${colorPalette.dark}) border-box`,\n },\n }\n : {};\n\n// Glow effect\nconst glowStyles = glow\n ? {\n animation: `${glowAnimation} 2s ease-in-out infinite`,\n boxShadow: `0 0 10px ${alpha(colorPalette.main, 0.3)}`,\n }\n : {};\n\n return { ...glassStyles, ...gradientStyles, ...glowStyles };\n};\n","import Code from '@mui/icons-material/Code';\nimport FormatBold from '@mui/icons-material/FormatBold';\nimport FormatColorFill from '@mui/icons-material/FormatColorFill';\nimport FormatColorText from '@mui/icons-material/FormatColorText';\nimport FormatItalic from '@mui/icons-material/FormatItalic';\nimport FormatListBulleted from '@mui/icons-material/FormatListBulleted';\nimport FormatListNumbered from '@mui/icons-material/FormatListNumbered';\nimport FormatQuote from '@mui/icons-material/FormatQuote';\nimport FormatUnderlined from '@mui/icons-material/FormatUnderlined';\nimport Link from '@mui/icons-material/Link';\nimport { alpha, Box, Divider, IconButton, Tooltip } from '@mui/material';\nimport type { Theme } from '@mui/material';\nimport { styled } from '@mui/material';\nimport React, { useEffect, useRef, useState } from 'react';\n\nimport { floatAnimation, getColorFromTheme } from './Textarea.styles';\nimport type { RichEditorToolbarCopy } from '../../../copy';\n\nconst RichToolbar = styled(Box)<{ glass?: boolean }>(({ theme, glass }) => ({\n display: 'flex',\n alignItems: 'center',\n gap: theme.spacing(0.5),\n padding: theme.spacing(1),\n borderRadius: `${theme.spacing(1)} ${theme.spacing(1)} 0 0`,\n borderBottom: `1px solid ${theme.palette.divider}`,\n background: glass ? alpha(theme.palette.background.paper, 0.1) : theme.palette.background.paper,\n ...(glass && {\n backdropFilter: 'blur(15px)' }),\n '& .MuiDivider-root': {\n height: 24,\n margin: `0 ${theme.spacing(1)}` } }));\n\nconst ToolbarButton = styled(IconButton, {\n shouldForwardProp: (prop) => prop !== 'active' })<{ active?: boolean }>(({ theme, active }) => ({\n padding: theme.spacing(0.75),\n borderRadius: theme.spacing(0.5),\n color: active ? theme.palette.primary.main : theme.palette.text.secondary,\n backgroundColor: active ? alpha(theme.palette.primary.main, 0.1) : 'transparent',\n transition: 'all 0.2s ease',\n position: 'relative',\n overflow: 'hidden',\n\n '&::before': {\n content: '\"\"',\n position: 'absolute',\n top: '50%',\n left: '50%',\n width: 0,\n height: 0,\n borderRadius: '50%',\n backgroundColor: alpha(theme.palette.primary.main, 0.3),\n transform: 'translate(-50%, -50%)',\n transition: 'width 0.3s, height 0.3s' },\n\n '&:hover': {\n color: theme.palette.primary.main,\n backgroundColor: alpha(theme.palette.primary.main, 0.15),\n transform: 'translateY(-1px)',\n animation: `${floatAnimation} 1.5s ease-in-out infinite`,\n\n '&::before': {\n width: '100%',\n height: '100%' } },\n\n '&:active': {\n transform: 'scale(0.95)' } }));\n\nconst CharacterCount = styled(Box)<{ limit?: number; count: number }>(({ theme, limit, count }) => {\n const isWarning = limit && count > limit * 0.8;\n const isError = limit && count > limit;\n\n return {\n position: 'absolute',\n bottom: theme.spacing(1),\n right: theme.spacing(1),\n fontSize: '0.75rem',\n color: isError\n ? theme.palette.error.main\n : isWarning\n ? theme.palette.warning.main\n : theme.palette.text.secondary,\n padding: '2px 6px',\n borderRadius: theme.spacing(0.5),\n backgroundColor: alpha(theme.palette.background.paper, 0.8),\n backdropFilter: 'blur(10px)',\n transition: 'all 0.3s ease' };\n});\n\nconst ContentEditableDiv = styled('div')<{\n error?: boolean;\n focused?: boolean;\n glass?: boolean;\n customColor?: string;\n theme?: Theme;\n}>(({ theme, error, focused, glass, customColor = 'primary' }) => {\n if (!theme) return {};\n const colorPalette = getColorFromTheme(theme, customColor);\n const errorColor = theme.palette.error;\n\n return {\n minHeight: '120px',\n padding: theme.spacing(1.5),\n borderRadius: `0 0 ${theme.spacing(1)} ${theme.spacing(1)}`,\n border: `2px solid ${error ? errorColor.main : focused ? colorPalette.main : theme.palette.divider}`,\n borderTop: 'none',\n backgroundColor: glass\n ? alpha(theme.palette.background.paper, 0.1)\n : theme.palette.background.paper,\n color: theme.palette.text.primary,\n fontFamily: theme.typography.fontFamily,\n fontSize: '1rem',\n lineHeight: 1.5,\n outline: 'none',\n transition: 'all 0.3s ease',\n cursor: 'text',\n\n ...(glass && {\n backdropFilter: 'blur(20px)' }),\n\n '&:hover': {\n backgroundColor: glass\n ? alpha(theme.palette.background.paper, 0.15)\n : alpha(theme.palette.background.paper, 0.9) },\n\n '&:focus': {\n borderColor: error ? errorColor.main : colorPalette.main,\n boxShadow: `0 0 0 3px ${alpha(error ? errorColor.main : colorPalette.main, 0.1)}` },\n\n '& > *': {\n margin: '0.5em 0' },\n\n '&[contenteditable=\"true\"]:empty::before': {\n content: 'attr(data-placeholder)',\n color: theme.palette.text.secondary,\n opacity: 0.6 } };\n});\n\n// Rich text formatting functions\nconst formatText = (command: string, value?: string) => {\n document.execCommand(command, false, value);\n};\n\n// Editable state: what is typed, how long it is, and which formats apply at the\n// caret. Kept apart from the markup below.\nconst useRichEditorState = ({\n value,\n onChange }: {\n value?: string;\n onChange?: (html: string) => void;\n}) => {\n const [focused, setFocused] = useState(false);\n const [characterCount, setCharacterCount] = useState(0);\n const [activeFormats, setActiveFormats] = useState({\n bold: false,\n italic: false,\n underline: false,\n list: false,\n orderedList: false,\n quote: false,\n code: false });\n const contentRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n if (contentRef.current && value !== undefined) {\n if (contentRef.current.innerHTML !== value) {\n contentRef.current.innerHTML = value;\n }\n setCharacterCount(contentRef.current.innerText.length);\n }\n }, [value]);\n\n const handleInput = () => {\n if (contentRef.current) {\n const html = contentRef.current.innerHTML;\n onChange?.(html);\n setCharacterCount(contentRef.current.innerText.length);\n updateActiveFormats();\n }\n };\n\n const updateActiveFormats = () => {\n setActiveFormats({\n bold: document.queryCommandState('bold'),\n italic: document.queryCommandState('italic'),\n underline: document.queryCommandState('underline'),\n list: document.queryCommandState('insertUnorderedList'),\n orderedList: document.queryCommandState('insertOrderedList'),\n quote: false,\n code: false });\n };\n\n const handleFormat = (command: string, value?: string) => {\n formatText(command, value);\n updateActiveFormats();\n contentRef.current?.focus();\n };\n\n return {\n focused,\n setFocused,\n characterCount,\n activeFormats,\n contentRef,\n handleInput,\n handleFormat,\n updateActiveFormats };\n};\n\nconst InlineFormatButtons: React.FC<{\n copy: RichEditorToolbarCopy;\n activeFormats: Record<string, boolean>;\n onCommand: (command: string, value?: string) => void;\n}> = ({ copy, activeFormats, onCommand }) => (\n <>\n <Tooltip title={copy.bold} arrow>\n <ToolbarButton\n size=\"small\"\n active={activeFormats.bold}\n onClick={() => onCommand('bold')}\n >\n <FormatBold fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Tooltip title={copy.italic} arrow>\n <ToolbarButton\n size=\"small\"\n active={activeFormats.italic}\n onClick={() => onCommand('italic')}\n >\n <FormatItalic fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Tooltip title={copy.underline} arrow>\n <ToolbarButton\n size=\"small\"\n active={activeFormats.underline}\n onClick={() => onCommand('underline')}\n >\n <FormatUnderlined fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n </>\n);\n\nconst BlockFormatButtons: React.FC<{\n copy: RichEditorToolbarCopy;\n activeFormats: Record<string, boolean>;\n onCommand: (command: string, value?: string) => void;\n}> = ({ copy, activeFormats, onCommand }) => (\n <>\n <Divider orientation=\"vertical\" flexItem />\n\n <Tooltip title={copy.bulletList} arrow>\n <ToolbarButton\n size=\"small\"\n active={activeFormats.list}\n onClick={() => onCommand('insertUnorderedList')}\n >\n <FormatListBulleted fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Tooltip title={copy.numberedList} arrow>\n <ToolbarButton\n size=\"small\"\n active={activeFormats.orderedList}\n onClick={() => onCommand('insertOrderedList')}\n >\n <FormatListNumbered fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Divider orientation=\"vertical\" flexItem />\n\n <Tooltip title={copy.quote} arrow>\n <ToolbarButton size=\"small\" onClick={() => onCommand('formatBlock', 'blockquote')}>\n <FormatQuote fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Tooltip title={copy.code} arrow>\n <ToolbarButton size=\"small\" onClick={() => onCommand('formatBlock', 'pre')}>\n <Code fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Divider orientation=\"vertical\" flexItem />\n\n <Tooltip title={copy.insertLink} arrow>\n <ToolbarButton\n size=\"small\"\n onClick={() => {\n const url = window.prompt(copy.linkPrompt);\n if (url) onCommand('createLink', url);\n }}\n >\n <Link fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Tooltip title={copy.textColor} arrow>\n <ToolbarButton size=\"small\">\n <FormatColorText fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n\n <Tooltip title={copy.backgroundColor} arrow>\n <ToolbarButton size=\"small\">\n <FormatColorFill fontSize=\"small\" />\n </ToolbarButton>\n </Tooltip>\n </>\n);\n\n// The formatting buttons. Split out so the editor body is state, handlers and\n// the editable region.\nconst EditorToolbar: React.FC<{\n copy: RichEditorToolbarCopy;\n glass?: boolean;\n activeFormats: Record<string, boolean>;\n onCommand: (command: string, value?: string) => void;\n}> = ({ copy, glass, activeFormats, onCommand }) => (\n <RichToolbar glass={glass}>\n <InlineFormatButtons copy={copy} activeFormats={activeFormats} onCommand={onCommand} />\n <BlockFormatButtons copy={copy} activeFormats={activeFormats} onCommand={onCommand} />\n </RichToolbar>\n);\n\nexport const TextareaRichEditor: React.FC<{\n /**\n * The ten toolbar buttons' tooltips — which are also their accessible names,\n * since every one of them is a glyph — plus the link prompt. REQUIRED: this\n * package ships no default copy.\n */\n copy: RichEditorToolbarCopy;\n value?: string;\n onChange?: (html: string) => void;\n placeholder?: string;\n error?: boolean;\n glass?: boolean;\n color?: string;\n characterLimit?: number;\n}> = ({ copy, value, onChange, placeholder, error, glass, color, characterLimit }) => {\n const {\n focused,\n setFocused,\n characterCount,\n activeFormats,\n contentRef,\n handleInput,\n handleFormat,\n updateActiveFormats } = useRichEditorState({ value, onChange });\n\n return (\n <Box sx={{ position: 'relative' }}>\n <EditorToolbar copy={copy} glass={glass} activeFormats={activeFormats} onCommand={handleFormat} />\n\n <ContentEditableDiv\n ref={contentRef}\n contentEditable\n data-placeholder={placeholder}\n error={error}\n focused={focused}\n glass={glass}\n customColor={color}\n onFocus={() => setFocused(true)}\n onBlur={() => setFocused(false)}\n onInput={handleInput}\n onKeyUp={updateActiveFormats}\n onMouseUp={updateActiveFormats}\n role=\"textbox\"\n aria-multiline=\"true\"\n aria-label={placeholder || 'Rich text editor'}\n />\n\n {characterLimit && (\n <CharacterCount limit={characterLimit} count={characterCount}>\n {characterCount}\n {characterLimit ? `/${characterLimit}` : ''}\n </CharacterCount>\n )}\n </Box>\n );\n};\n"],"mappings":"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","names":["alpha","Box","styled","useState","alpha","alpha","value","jsx","jsxs","styled","alpha","Box","useState"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/form/Textarea/Textarea.tsx"],"sourcesContent":["import {\n alpha,\n Box,\n FormHelperText,\n InputLabel,\n TextareaAutosize } from '@mui/material';\nimport type { Theme } from '@mui/material';\nimport { styled } from '@mui/material';\nimport React, { lazy, Suspense, useState } from 'react';\n\nimport {\n getColorFromTheme,\n textareaEmphasisStyles } from './Textarea.styles';\n\n/**\n * The rich editor is LOADED ON DEMAND, not imported (12-51 follow-up).\n *\n * It renders only for `variant === 'rich'`, but a static import made every\n * consumer of a plain `<Textarea>` pay for it — 387 lines of toolbar plus the\n * MUI `Tooltip`, `IconButton` and `Divider` its formatting buttons need. That\n * is not theoretical: an adopting storefront imports this entry for a one-line\n * order note, and `Tooltip` (8.7 KiB) reached its EAGER chunk through here\n * while nothing on that critical path renders a tooltip at all.\n *\n * `variant` is a prop, so the branch cannot be decided at build time and no\n * amount of tree-shaking removes it. A lazy boundary is what expresses \"only\n * the rich variant pays\", and it costs the rich variant one microtask it can\n * afford — it is a text editor a user has already chosen to open, never a\n * first-paint element.\n */\nconst TextareaRichEditor = lazy(async () => ({\n default: (await import('./TextareaRichEditor')).TextareaRichEditor }));\nimport type { TextareaProps } from './Textarea.types';\n\n// Interface for styled component props\ninterface StyledTextareaProps {\n customVariant?: string;\n customColor?: string;\n customSize?: string;\n glow?: boolean;\n glass?: boolean;\n gradient?: boolean;\n error?: boolean;\n theme?: Theme;\n}\n\n// Glow animation for enhanced visual effects\nconst StyledTextarea = styled(TextareaAutosize, {\n shouldForwardProp: (prop) =>\n !['customVariant', 'customColor', 'customSize', 'glow', 'glass', 'gradient', 'error'].includes(\n prop as string,\n ) })<StyledTextareaProps>(({\n theme,\n customVariant,\n customColor = 'primary',\n customSize = 'md',\n glow,\n glass,\n gradient,\n error }) => {\n if (!theme) return {};\n const colorPalette = getColorFromTheme(theme, customColor);\n const errorColor = theme.palette.error;\n\n const sizeMap = {\n xs: { padding: '6px 8px', fontSize: '0.75rem', minHeight: '60px' },\n sm: { padding: '8px 10px', fontSize: '0.875rem', minHeight: '80px' },\n md: { padding: '10px 12px', fontSize: '1rem', minHeight: '100px' },\n lg: { padding: '12px 14px', fontSize: '1.125rem', minHeight: '120px' },\n xl: { padding: '14px 16px', fontSize: '1.25rem', minHeight: '140px' } };\n\n const baseStyles = {\n width: '100%',\n fontFamily: theme.typography.fontFamily,\n borderRadius: theme.spacing(1),\n border: `2px solid ${error ? errorColor.main : theme.palette.divider}`,\n backgroundColor: theme.palette.background.paper,\n color: theme.palette.text.primary,\n transition: 'all 0.3s ease',\n resize: customVariant === 'resizable' ? 'vertical' : 'none',\n outline: 'none',\n ...sizeMap[customSize as keyof typeof sizeMap],\n\n '&::placeholder': {\n color: theme.palette.text.secondary,\n opacity: 0.7 },\n\n '&:hover': {\n borderColor: error ? errorColor.dark : colorPalette.main,\n backgroundColor: alpha(theme.palette.background.paper, 0.8) },\n\n '&:focus': {\n borderColor: error ? errorColor.main : colorPalette.main,\n backgroundColor: theme.palette.background.paper,\n boxShadow: `0 0 0 3px ${alpha(error ? errorColor.main : colorPalette.main, 0.1)}` } };\n\n // Glass morphism effect\n return {\n ...baseStyles,\n ...textareaEmphasisStyles({ theme, colorPalette, glass, gradient, glow }) };\n});\n\nconst StyledLabel = styled(InputLabel, {\n shouldForwardProp: (prop) => !['glass', 'error'].includes(prop as string) })<{ glass?: boolean; error?: boolean }>(({ theme, glass, error }) => ({\n marginBottom: theme.spacing(1),\n fontWeight: 500,\n color: error ? theme.palette.error.main : theme.palette.text.primary,\n ...(glass && {\n backgroundColor: alpha(theme.palette.background.paper, 0.1),\n backdropFilter: 'blur(10px)',\n padding: '4px 8px',\n borderRadius: '4px',\n border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,\n display: 'inline-block' }) }));\n\n// IconWrapper - replaced with inline Box component for better TypeScript compatibility\n// const IconWrapper = styled(Box)<{ position: 'start' | 'end' }>(({ theme, position }) => {\n// const positionStyles = position === 'start' ? { left: '12px' } : { right: '12px' };\n// return {\n// position: 'absolute' as const,\n// top: '12px',\n// ...positionStyles,\n// color: theme.palette.text.secondary,\n// pointerEvents: 'none' as const,\n// zIndex: 1,\n// };\n// });\n\n// Rich text toolbar styling\n\nconst ICON_GUTTER = '40px';\n\nconst TEXTAREA_DEFAULTS = {\n variant: 'default',\n color: 'primary',\n size: 'md',\n error: false,\n glassLabel: false,\n glow: false,\n glass: false,\n gradient: false,\n iconPosition: 'start',\n minRows: 3 } satisfies Partial<TextareaProps>;\n\n// Strips explicitly-undefined props before the merge, so `prop={undefined}`\n// still falls back to the default as a destructuring default would.\nconst definedProps = (props: TextareaProps): Partial<TextareaProps> =>\n Object.fromEntries(\n Object.entries(props).filter(([, value]) => value !== undefined),\n ) as Partial<TextareaProps>;\n\n// Label above, helper text below — identical for the plain and rich variants,\n// which previously duplicated both.\nconst TextareaShell: React.FC<{\n testId?: string;\n label?: React.ReactNode;\n glassLabel: boolean;\n error: boolean;\n helperText?: React.ReactNode;\n children: React.ReactNode;\n}> = ({ testId, label, glassLabel, error, helperText, children }) => (\n <Box\n sx={{ position: 'relative', width: '100%' }}\n data-testid={testId ? `${testId}-wrapper` : undefined}\n >\n {label && (\n <StyledLabel glass={glassLabel} error={error}>\n {label}\n </StyledLabel>\n )}\n\n {children}\n\n {helperText && (\n <FormHelperText error={error} sx={{ mt: 1 }}>\n {helperText}\n </FormHelperText>\n )}\n </Box>\n);\n\nconst TextareaIcon: React.FC<{\n icon?: React.ReactNode;\n iconPosition: 'start' | 'end';\n}> = ({ icon, iconPosition }) => (\n <Box\n sx={{\n position: 'absolute',\n top: '12px',\n ...(iconPosition === 'start' ? { left: '12px' } : { right: '12px' }),\n color: 'text.secondary',\n pointerEvents: 'none',\n zIndex: 1 }}\n >\n {icon}\n </Box>\n);\n\n/**\n * The rich variant, kept out of the main component's body.\n *\n * Two reasons rather than one: the `Suspense` boundary and its props belong\n * together with the lazy import above, and the component that renders every\n * OTHER variant stays inside the size the complexity gate allows without\n * anything being suppressed.\n */\nfunction RichVariant({\n shell,\n editor }: {\n shell: Omit<React.ComponentProps<typeof TextareaShell>, 'children'>;\n editor: React.ComponentProps<typeof TextareaRichEditor>;\n}): React.JSX.Element {\n return (\n <TextareaShell {...shell}>\n {/*\n `null` rather than a spinner: the editor resolves from the same document\n the caller is already rendering, so a fallback would flash for a frame\n and read as a glitch rather than as loading.\n */}\n <Suspense fallback={null}>\n <TextareaRichEditor {...editor} />\n </Suspense>\n </TextareaShell>\n );\n}\n\nexport const Textarea: React.FC<TextareaProps> = (textareaProps) => {\n const {\n variant,\n color,\n size,\n error,\n helperText,\n label,\n glassLabel,\n glow,\n glass,\n gradient,\n icon,\n iconPosition,\n minRows,\n maxRows,\n style,\n 'data-testid': dataTestId,\n dataTestId: dataTestIdCamelCase,\n ...props\n } = { ...TEXTAREA_DEFAULTS, ...definedProps(textareaProps) };\n\n // Read off the props rather than the merged object: the spread widens every\n // key to optional, which would lose what the variant union already proved.\n const { richEditorCopy } = textareaProps;\n\n const [richTextValue, setRichTextValue] = useState('');\n const hasIcon = Boolean(icon);\n const testId = dataTestId || dataTestIdCamelCase;\n\n const textareaStyle = {\n ...style,\n // Same gap either side; only which side it applies to changes. The previous\n // version had `iconPosition === 'start' ? '40px' : '40px'`, a ternary whose\n // two branches were identical.\n ...(hasIcon && {\n [iconPosition === 'start' ? 'paddingLeft' : 'paddingRight']: ICON_GUTTER }) };\n\n // If rich text variant, use the rich text editor\n if (variant === 'rich' && richEditorCopy) {\n return (\n <RichVariant\n shell={{ testId, label, glassLabel, error, helperText }}\n editor={{\n copy: richEditorCopy,\n value: richTextValue,\n onChange: setRichTextValue,\n placeholder: props.placeholder,\n error,\n glass,\n color,\n characterLimit: props.maxLength }}\n />\n );\n }\n\n return (\n <TextareaShell testId={testId} label={label} glassLabel={glassLabel} error={error} helperText={helperText}>\n <Box sx={{ position: 'relative' }}>\n {hasIcon && <TextareaIcon icon={icon} iconPosition={iconPosition} />}\n\n <StyledTextarea\n customVariant={variant}\n customColor={color}\n customSize={size}\n glow={glow}\n glass={glass}\n gradient={gradient}\n error={error}\n minRows={variant === 'autosize' ? minRows : undefined}\n maxRows={variant === 'autosize' ? maxRows : undefined}\n style={textareaStyle}\n aria-label={label || props['aria-label']}\n data-testid={testId}\n {...props}\n />\n </Box>\n </TextareaShell>\n );\n};\n\nTextarea.displayName = 'Textarea';\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Textarea.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/Textarea/Textarea.tsx"],"names":[],"mappings":"AAQA,OAAO,
|
|
1
|
+
{"version":3,"file":"Textarea.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/Textarea/Textarea.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAmC,MAAM,OAAO,CAAC;AAwBxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAkMtD,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CA+E5C,CAAC"}
|
package/package.json
CHANGED
|
@@ -6,12 +6,30 @@ import {
|
|
|
6
6
|
TextareaAutosize } from '@mui/material';
|
|
7
7
|
import type { Theme } from '@mui/material';
|
|
8
8
|
import { styled } from '@mui/material';
|
|
9
|
-
import React, { useState } from 'react';
|
|
9
|
+
import React, { lazy, Suspense, useState } from 'react';
|
|
10
10
|
|
|
11
11
|
import {
|
|
12
12
|
getColorFromTheme,
|
|
13
13
|
textareaEmphasisStyles } from './Textarea.styles';
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The rich editor is LOADED ON DEMAND, not imported (12-51 follow-up).
|
|
17
|
+
*
|
|
18
|
+
* It renders only for `variant === 'rich'`, but a static import made every
|
|
19
|
+
* consumer of a plain `<Textarea>` pay for it — 387 lines of toolbar plus the
|
|
20
|
+
* MUI `Tooltip`, `IconButton` and `Divider` its formatting buttons need. That
|
|
21
|
+
* is not theoretical: an adopting storefront imports this entry for a one-line
|
|
22
|
+
* order note, and `Tooltip` (8.7 KiB) reached its EAGER chunk through here
|
|
23
|
+
* while nothing on that critical path renders a tooltip at all.
|
|
24
|
+
*
|
|
25
|
+
* `variant` is a prop, so the branch cannot be decided at build time and no
|
|
26
|
+
* amount of tree-shaking removes it. A lazy boundary is what expresses "only
|
|
27
|
+
* the rich variant pays", and it costs the rich variant one microtask it can
|
|
28
|
+
* afford — it is a text editor a user has already chosen to open, never a
|
|
29
|
+
* first-paint element.
|
|
30
|
+
*/
|
|
31
|
+
const TextareaRichEditor = lazy(async () => ({
|
|
32
|
+
default: (await import('./TextareaRichEditor')).TextareaRichEditor }));
|
|
15
33
|
import type { TextareaProps } from './Textarea.types';
|
|
16
34
|
|
|
17
35
|
// Interface for styled component props
|
|
@@ -178,6 +196,34 @@ const TextareaIcon: React.FC<{
|
|
|
178
196
|
</Box>
|
|
179
197
|
);
|
|
180
198
|
|
|
199
|
+
/**
|
|
200
|
+
* The rich variant, kept out of the main component's body.
|
|
201
|
+
*
|
|
202
|
+
* Two reasons rather than one: the `Suspense` boundary and its props belong
|
|
203
|
+
* together with the lazy import above, and the component that renders every
|
|
204
|
+
* OTHER variant stays inside the size the complexity gate allows without
|
|
205
|
+
* anything being suppressed.
|
|
206
|
+
*/
|
|
207
|
+
function RichVariant({
|
|
208
|
+
shell,
|
|
209
|
+
editor }: {
|
|
210
|
+
shell: Omit<React.ComponentProps<typeof TextareaShell>, 'children'>;
|
|
211
|
+
editor: React.ComponentProps<typeof TextareaRichEditor>;
|
|
212
|
+
}): React.JSX.Element {
|
|
213
|
+
return (
|
|
214
|
+
<TextareaShell {...shell}>
|
|
215
|
+
{/*
|
|
216
|
+
`null` rather than a spinner: the editor resolves from the same document
|
|
217
|
+
the caller is already rendering, so a fallback would flash for a frame
|
|
218
|
+
and read as a glitch rather than as loading.
|
|
219
|
+
*/}
|
|
220
|
+
<Suspense fallback={null}>
|
|
221
|
+
<TextareaRichEditor {...editor} />
|
|
222
|
+
</Suspense>
|
|
223
|
+
</TextareaShell>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
181
227
|
export const Textarea: React.FC<TextareaProps> = (textareaProps) => {
|
|
182
228
|
const {
|
|
183
229
|
variant,
|
|
@@ -219,18 +265,18 @@ export const Textarea: React.FC<TextareaProps> = (textareaProps) => {
|
|
|
219
265
|
// If rich text variant, use the rich text editor
|
|
220
266
|
if (variant === 'rich' && richEditorCopy) {
|
|
221
267
|
return (
|
|
222
|
-
<
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
268
|
+
<RichVariant
|
|
269
|
+
shell={{ testId, label, glassLabel, error, helperText }}
|
|
270
|
+
editor={{
|
|
271
|
+
copy: richEditorCopy,
|
|
272
|
+
value: richTextValue,
|
|
273
|
+
onChange: setRichTextValue,
|
|
274
|
+
placeholder: props.placeholder,
|
|
275
|
+
error,
|
|
276
|
+
glass,
|
|
277
|
+
color,
|
|
278
|
+
characterLimit: props.maxLength }}
|
|
279
|
+
/>
|
|
234
280
|
);
|
|
235
281
|
}
|
|
236
282
|
|