@arkyn/components 3.0.4 → 3.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/richText/index.d.ts +10 -1
- package/dist/components/richText/index.d.ts.map +1 -1
- package/dist/components/richText/insertLink/index.d.ts +5 -0
- package/dist/components/richText/insertLink/index.d.ts.map +1 -0
- package/dist/components/richText/insertVideo/index.d.ts.map +1 -1
- package/dist/components/richText/leaf/index.d.ts +1 -0
- package/dist/components/richText/leaf/index.d.ts.map +1 -1
- package/dist/index.js +710 -633
- package/dist/index.js.map +1 -1
- package/dist/modules/components/richText/index.js +58 -56
- package/dist/modules/components/richText/index.js.map +1 -1
- package/dist/modules/components/richText/insertLink/index.js +68 -0
- package/dist/modules/components/richText/insertLink/index.js.map +1 -0
- package/dist/modules/components/richText/insertLink/styles.css +1 -0
- package/dist/modules/components/richText/insertVideo/index.js +45 -41
- package/dist/modules/components/richText/insertVideo/index.js.map +1 -1
- package/dist/modules/components/richText/leaf/index.js +6 -1
- package/dist/modules/components/richText/leaf/index.js.map +1 -1
- package/dist/modules/components/richText/leaf/styles.css +1 -0
- package/dist/modules/components/richText/styles.css +4 -0
- package/dist/modules/services/isValidHttpsUrl.js +12 -0
- package/dist/modules/services/isValidHttpsUrl.js.map +1 -0
- package/dist/modules/utils/richTextUtilities.js +24 -17
- package/dist/modules/utils/richTextUtilities.js.map +1 -1
- package/dist/services/isValidHttpsUrl.d.ts +3 -0
- package/dist/services/isValidHttpsUrl.d.ts.map +1 -0
- package/dist/style.css +1 -1
- package/dist/types/richTextTypes.d.ts +13 -2
- package/dist/types/richTextTypes.d.ts.map +1 -1
- package/dist/utils/richTextUtilities.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -6,133 +6,135 @@ import { hotKeys as i, initialValue as a } from "../../templates/richTextTemplat
|
|
|
6
6
|
import { BlockButton as o } from "./blockButton/index.js";
|
|
7
7
|
import { Element as s } from "./element/index.js";
|
|
8
8
|
import { InsertImage as c } from "./insertImage/index.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
9
|
+
import { InsertLink as ee } from "./insertLink/index.js";
|
|
10
|
+
import { InsertVideo as te } from "./insertVideo/index.js";
|
|
11
|
+
import { Leaf as ne } from "./leaf/index.js";
|
|
12
|
+
import { MarkButton as l } from "./markButton/index.js";
|
|
13
|
+
import { Toolbar as u } from "./toolbar/index.js";
|
|
13
14
|
/* empty css */
|
|
14
|
-
import { useCallback as
|
|
15
|
-
import { jsx as
|
|
16
|
-
import { AlignCenter as
|
|
15
|
+
import { useCallback as d, useId as f, useMemo as p, useRef as re, useState as m } from "react";
|
|
16
|
+
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
17
|
+
import { AlignCenter as ie, AlignJustify as ae, AlignLeft as oe, AlignRight as se, Bold as ce, Code as _, Heading1 as v, Heading2 as y, Italic as b, Quote as x, Underline as S } from "lucide-react";
|
|
17
18
|
import C from "is-hotkey";
|
|
18
19
|
import { createEditor as w } from "slate";
|
|
19
20
|
import { withHistory as T } from "slate-history";
|
|
20
21
|
import { Editable as E, Slate as D, withReact as O } from "slate-react";
|
|
21
22
|
//#region src/components/richText/index.tsx
|
|
22
23
|
function k(k) {
|
|
23
|
-
let { name: A, hiddenButtons: j, imageConfig: M, videoConfig: N,
|
|
24
|
+
let { name: A, hiddenButtons: j, imageConfig: M, videoConfig: N, linkConfig: P, className: F = "", defaultValue: I = "[]", enforceCharacterLimit: L = !1, onChangeCharactersCount: R, baseErrorMessage: z, maxLimit: B = 1e4, onChange: le, isError: V, label: H, showAsterisk: U, id: W, orientation: ue = "vertical", unShowFieldTemplate: de = !1 } = k, G = p(() => T(O(w())), []), { fieldErrors: fe } = e(), K = p(() => {
|
|
24
25
|
try {
|
|
25
|
-
let e = JSON.parse(
|
|
26
|
+
let e = JSON.parse(I);
|
|
26
27
|
return !Array.isArray(e) || e.length === 0 ? structuredClone(a) : e.every((e) => typeof e == "object" && !!e && "type" in e && "children" in e) ? e : structuredClone(a);
|
|
27
28
|
} catch {
|
|
28
29
|
return structuredClone(a);
|
|
29
30
|
}
|
|
30
|
-
}, [
|
|
31
|
+
}, [I]), [q, pe] = m(n(K).length), [me, he] = m(JSON.stringify(K) || "[]"), [ge, J] = m(!1), _e = re(null), Y = f(), ve = W || Y, X = z || fe?.[A], ye = V || !!X, be = d(ne, []), xe = d(s, []);
|
|
31
32
|
function Z(e) {
|
|
32
33
|
let t = n(e);
|
|
33
|
-
|
|
34
|
+
pe(t.length), R?.(t.length), !(L && t.length >= B) && (he(JSON.stringify(e)), le?.(e));
|
|
34
35
|
}
|
|
35
|
-
let
|
|
36
|
+
let Se = `arkynRichText ${ye || B < q ? "errorTrue" : "errorFalse"} ${ge ? "focusTrue" : "focusFalse"}`, Q = B - q;
|
|
36
37
|
function $(e) {
|
|
37
38
|
return !j?.includes(e);
|
|
38
39
|
}
|
|
39
|
-
return /* @__PURE__ */
|
|
40
|
+
return /* @__PURE__ */ h(t, {
|
|
40
41
|
name: A,
|
|
41
|
-
label:
|
|
42
|
-
showAsterisk:
|
|
43
|
-
className:
|
|
42
|
+
label: H,
|
|
43
|
+
showAsterisk: U,
|
|
44
|
+
className: F,
|
|
44
45
|
errorMessage: X,
|
|
45
|
-
unShowFieldTemplate:
|
|
46
|
-
orientation:
|
|
47
|
-
children: /* @__PURE__ */
|
|
48
|
-
editor:
|
|
49
|
-
initialValue:
|
|
46
|
+
unShowFieldTemplate: de,
|
|
47
|
+
orientation: ue,
|
|
48
|
+
children: /* @__PURE__ */ g(D, {
|
|
49
|
+
editor: G,
|
|
50
|
+
initialValue: K,
|
|
50
51
|
onChange: Z,
|
|
51
52
|
onValueChange: Z,
|
|
52
53
|
children: [
|
|
53
|
-
/* @__PURE__ */
|
|
54
|
-
className:
|
|
54
|
+
/* @__PURE__ */ g("div", {
|
|
55
|
+
className: Se,
|
|
55
56
|
children: [
|
|
56
|
-
/* @__PURE__ */
|
|
57
|
-
$("headingOne") && /* @__PURE__ */
|
|
57
|
+
/* @__PURE__ */ g(u, { children: [
|
|
58
|
+
$("headingOne") && /* @__PURE__ */ h(o, {
|
|
58
59
|
format: "headingOne",
|
|
59
60
|
icon: v
|
|
60
61
|
}),
|
|
61
|
-
$("headingTwo") && /* @__PURE__ */
|
|
62
|
+
$("headingTwo") && /* @__PURE__ */ h(o, {
|
|
62
63
|
format: "headingTwo",
|
|
63
64
|
icon: y
|
|
64
65
|
}),
|
|
65
|
-
$("blockQuote") && /* @__PURE__ */
|
|
66
|
+
$("blockQuote") && /* @__PURE__ */ h(o, {
|
|
66
67
|
format: "blockQuote",
|
|
67
68
|
icon: x
|
|
68
69
|
}),
|
|
69
|
-
$("bold") && /* @__PURE__ */
|
|
70
|
+
$("bold") && /* @__PURE__ */ h(l, {
|
|
70
71
|
format: "bold",
|
|
71
|
-
icon:
|
|
72
|
+
icon: ce
|
|
72
73
|
}),
|
|
73
|
-
$("italic") && /* @__PURE__ */
|
|
74
|
+
$("italic") && /* @__PURE__ */ h(l, {
|
|
74
75
|
format: "italic",
|
|
75
76
|
icon: b
|
|
76
77
|
}),
|
|
77
|
-
$("underline") && /* @__PURE__ */
|
|
78
|
+
$("underline") && /* @__PURE__ */ h(l, {
|
|
78
79
|
format: "underline",
|
|
79
80
|
icon: S
|
|
80
81
|
}),
|
|
81
|
-
$("code") && /* @__PURE__ */
|
|
82
|
+
$("code") && /* @__PURE__ */ h(l, {
|
|
82
83
|
format: "code",
|
|
83
|
-
icon:
|
|
84
|
+
icon: _
|
|
84
85
|
}),
|
|
85
|
-
$("left") && /* @__PURE__ */
|
|
86
|
+
$("left") && /* @__PURE__ */ h(o, {
|
|
86
87
|
format: "left",
|
|
87
|
-
icon:
|
|
88
|
+
icon: oe
|
|
88
89
|
}),
|
|
89
|
-
$("right") && /* @__PURE__ */
|
|
90
|
+
$("right") && /* @__PURE__ */ h(o, {
|
|
90
91
|
format: "right",
|
|
91
|
-
icon:
|
|
92
|
+
icon: se
|
|
92
93
|
}),
|
|
93
|
-
$("center") && /* @__PURE__ */
|
|
94
|
+
$("center") && /* @__PURE__ */ h(o, {
|
|
94
95
|
format: "center",
|
|
95
|
-
icon:
|
|
96
|
+
icon: ie
|
|
96
97
|
}),
|
|
97
|
-
$("justify") && /* @__PURE__ */
|
|
98
|
+
$("justify") && /* @__PURE__ */ h(o, {
|
|
98
99
|
format: "justify",
|
|
99
|
-
icon:
|
|
100
|
+
icon: ae
|
|
100
101
|
}),
|
|
101
|
-
M && $("image") && /* @__PURE__ */
|
|
102
|
-
$("video") && /* @__PURE__ */
|
|
102
|
+
M && $("image") && /* @__PURE__ */ h(c, { ...M }),
|
|
103
|
+
$("video") && /* @__PURE__ */ h(te, { ...N }),
|
|
104
|
+
$("link") && /* @__PURE__ */ h(ee, { ...P })
|
|
103
105
|
] }),
|
|
104
|
-
/* @__PURE__ */
|
|
106
|
+
/* @__PURE__ */ h(E, {
|
|
105
107
|
className: "editorContainer",
|
|
106
|
-
renderElement:
|
|
107
|
-
renderLeaf:
|
|
108
|
+
renderElement: xe,
|
|
109
|
+
renderLeaf: be,
|
|
108
110
|
spellCheck: !0,
|
|
109
|
-
ref:
|
|
110
|
-
id:
|
|
111
|
-
onFocus: () =>
|
|
112
|
-
onBlur: () =>
|
|
111
|
+
ref: _e,
|
|
112
|
+
id: ve,
|
|
113
|
+
onFocus: () => J(!0),
|
|
114
|
+
onBlur: () => J(!1),
|
|
113
115
|
onKeyDown: (e) => {
|
|
114
116
|
for (let t in i) if (C(t, e)) {
|
|
115
117
|
e.preventDefault();
|
|
116
118
|
let n = i[t];
|
|
117
|
-
r(
|
|
119
|
+
r(G, n);
|
|
118
120
|
}
|
|
119
121
|
}
|
|
120
122
|
}),
|
|
121
|
-
Q < 0 && /* @__PURE__ */
|
|
123
|
+
Q < 0 && /* @__PURE__ */ h("div", {
|
|
122
124
|
className: "restatesCharacters",
|
|
123
125
|
children: Q
|
|
124
126
|
})
|
|
125
127
|
]
|
|
126
128
|
}),
|
|
127
|
-
/* @__PURE__ */
|
|
129
|
+
/* @__PURE__ */ h("input", {
|
|
128
130
|
type: "hidden",
|
|
129
131
|
name: A,
|
|
130
|
-
value:
|
|
132
|
+
value: me.slice(0, B)
|
|
131
133
|
}),
|
|
132
|
-
/* @__PURE__ */
|
|
134
|
+
/* @__PURE__ */ h("input", {
|
|
133
135
|
type: "hidden",
|
|
134
136
|
name: `${A}Count`,
|
|
135
|
-
value:
|
|
137
|
+
value: q
|
|
136
138
|
})
|
|
137
139
|
]
|
|
138
140
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/richText/index.tsx"],"sourcesContent":["import isHotkey from \"is-hotkey\";\nimport {\n\tAlignCenter,\n\tAlignJustify,\n\tAlignLeft,\n\tAlignRight,\n\tBold,\n\tCode,\n\tHeading1,\n\tHeading2,\n\tItalic,\n\tQuote,\n\tUnderline,\n} from \"lucide-react\";\nimport { useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport { createEditor, type Descendant } from \"slate\";\nimport { withHistory } from \"slate-history\";\nimport { Editable, Slate, withReact } from \"slate-react\";\nimport { useForm } from \"../../hooks/useForm\";\nimport { extractTextFromNode } from \"../../services/extractTextFromNode\";\nimport { FieldTemplate } from \"../../services/fieldTemplate\";\nimport { toggleMark } from \"../../services/toggleMark\";\nimport { hotKeys, initialValue } from \"../../templates/richTextTemplates\";\nimport type {\n\tRichTextHiddenButtonKey,\n\tRichTextProps,\n} from \"../../types/richTextTypes\";\nimport { BlockButton } from \"./blockButton\";\nimport { Element } from \"./element\";\nimport { InsertImage } from \"./insertImage\";\nimport { InsertVideo } from \"./insertVideo\";\nimport { Leaf } from \"./leaf\";\nimport { MarkButton } from \"./markButton\";\nimport { Toolbar } from \"./toolbar\";\nimport \"./styles.css\";\n\n/**\n * RichText, WYSIWYG rich-text editor built on Slate.js with a configurable toolbar.\n *\n * **Toolbar features:** bold, italic, underline, code, H1/H2, block quote, alignment (left/center/right/justify), image and video insertion.\n *\n * Editor content is stored as a Slate JSON string in a hidden `<input>` for form submission.\n * Integrates with `useForm` to display validation errors by field name.\n *\n * @param props.name - Field name for form submission. Required.\n * @param props.label - Label text displayed above the editor.\n * @param props.hiddenButtons - Toolbar button keys to hide (e.g. `[\"image\", \"code\"]`).\n * @param props.imageConfig - Enables image insertion; contains the upload endpoint and modal labels.\n * @param props.defaultValue - Initial editor content as a Slate JSON string. Default: \"[]\"\n * @param props.maxLimit - Maximum character count. Default: 10000\n * @param props.enforceCharacterLimit - Prevents typing past `maxLimit`. Default: false\n * @param props.onChangeCharactersCount - Callback fired on every keystroke, receives the current character count.\n * @param props.onChange - Callback fired when editor content changes, receives the Slate `Descendant[]`.\n * @param props.baseErrorMessage - Custom error message (overrides `useForm` context error).\n * @param props.isError - Forces the error visual state.\n * @param props.id - Custom id for the editable area element.\n * @param props.showAsterisk - Appends `*` to the label.\n * @param props.orientation - Layout direction. Default: \"horizontal\"\n * @param props.unShowFieldTemplate - Skips wrapper, label, and error rendering. Default: false\n *\n * @returns RichText JSX element wrapped in `FieldTemplate`.\n *\n * @example\n * ```tsx\n * // Basic\n * <RichText name=\"content\" />\n *\n * // With character limit and hidden toolbar buttons\n * <RichText\n * name=\"description\"\n * label=\"Description\"\n * maxLimit={500}\n * enforceCharacterLimit\n * hiddenButtons={[\"image\", \"video\", \"code\"]}\n * onChangeCharactersCount={(n) => setCharCount(n)}\n * />\n *\n * // With image upload support\n * <RichText\n * name=\"article\"\n * label=\"Article Body\"\n * imageConfig={{ action: \"/api/upload\" }}\n * />\n * ```\n */\n\nfunction RichText(props: RichTextProps) {\n\tconst {\n\t\tname,\n\t\thiddenButtons,\n\t\timageConfig,\n\t\tvideoConfig,\n\t\tclassName: wrapperClassName = \"\",\n\t\tdefaultValue = \"[]\",\n\t\tenforceCharacterLimit = false,\n\t\tonChangeCharactersCount,\n\t\tbaseErrorMessage,\n\t\tmaxLimit = 10000,\n\t\tonChange,\n\t\tisError: baseIsError,\n\t\tlabel,\n\t\tshowAsterisk,\n\t\tid,\n\t\torientation = \"vertical\",\n\t\tunShowFieldTemplate = false,\n\t} = props;\n\n\tconst editor = useMemo(() => withHistory(withReact(createEditor())), []);\n\n\tconst { fieldErrors } = useForm();\n\n\tconst defaultNodes = useMemo((): Descendant[] => {\n\t\ttry {\n\t\t\tconst parsedNodes = JSON.parse(defaultValue);\n\t\t\tif (!Array.isArray(parsedNodes) || parsedNodes.length === 0) {\n\t\t\t\treturn structuredClone(initialValue);\n\t\t\t}\n\n\t\t\tconst isValidNodes = parsedNodes.every(\n\t\t\t\t(node) =>\n\t\t\t\t\ttypeof node === \"object\" &&\n\t\t\t\t\tnode !== null &&\n\t\t\t\t\t\"type\" in node &&\n\t\t\t\t\t\"children\" in node,\n\t\t\t);\n\n\t\t\treturn isValidNodes ? parsedNodes : structuredClone(initialValue);\n\t\t} catch (_error) {\n\t\t\treturn structuredClone(initialValue);\n\t\t}\n\t}, [defaultValue]);\n\n\tconst textFromNodes = extractTextFromNode(defaultNodes);\n\n\tconst [charactersCount, setCharactersCount] = useState(textFromNodes.length);\n\tconst [inputValue, setInputValue] = useState(\n\t\tJSON.stringify(defaultNodes) || \"[]\",\n\t);\n\n\tconst [onFocus, setOnFocus] = useState(false);\n\n\tconst ref = useRef<HTMLInputElement>(null);\n\tconst generatedId = useId();\n\tconst inputId = id || generatedId;\n\n\tconst errorMessage = baseErrorMessage || fieldErrors?.[name];\n\tconst isError = baseIsError || !!errorMessage;\n\n\tconst renderLeaf = useCallback(Leaf, []);\n\tconst renderElement = useCallback(Element, []);\n\n\tfunction handleChange(value: Descendant[]) {\n\t\tconst text = extractTextFromNode(value);\n\n\t\tsetCharactersCount(text.length);\n\t\tonChangeCharactersCount?.(text.length);\n\n\t\tif (enforceCharacterLimit && text.length >= maxLimit) {\n\t\t\treturn;\n\t\t}\n\n\t\tsetInputValue(JSON.stringify(value));\n\t\tonChange?.(value);\n\t}\n\n\tconst focusClass = onFocus ? \"focusTrue\" : \"focusFalse\";\n\tconst errorClass = isError\n\t\t? \"errorTrue\"\n\t\t: maxLimit < charactersCount\n\t\t\t? \"errorTrue\"\n\t\t\t: \"errorFalse\";\n\n\tconst className = `arkynRichText ${errorClass} ${focusClass}`;\n\n\tconst restatesCharacters = maxLimit - charactersCount;\n\n\tfunction buttonIsNotHidden(format: RichTextHiddenButtonKey) {\n\t\treturn !hiddenButtons?.includes(format);\n\t}\n\n\treturn (\n\t\t<FieldTemplate\n\t\t\tname={name}\n\t\t\tlabel={label}\n\t\t\tshowAsterisk={showAsterisk}\n\t\t\tclassName={wrapperClassName}\n\t\t\terrorMessage={errorMessage}\n\t\t\tunShowFieldTemplate={unShowFieldTemplate}\n\t\t\torientation={orientation}\n\t\t>\n\t\t\t<Slate\n\t\t\t\teditor={editor}\n\t\t\t\tinitialValue={defaultNodes}\n\t\t\t\tonChange={handleChange}\n\t\t\t\tonValueChange={handleChange}\n\t\t\t>\n\t\t\t\t<div className={className}>\n\t\t\t\t\t<Toolbar>\n\t\t\t\t\t\t{buttonIsNotHidden(\"headingOne\") && (\n\t\t\t\t\t\t\t<BlockButton format=\"headingOne\" icon={Heading1} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"headingTwo\") && (\n\t\t\t\t\t\t\t<BlockButton format=\"headingTwo\" icon={Heading2} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"blockQuote\") && (\n\t\t\t\t\t\t\t<BlockButton format=\"blockQuote\" icon={Quote} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"bold\") && (\n\t\t\t\t\t\t\t<MarkButton format=\"bold\" icon={Bold} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"italic\") && (\n\t\t\t\t\t\t\t<MarkButton format=\"italic\" icon={Italic} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"underline\") && (\n\t\t\t\t\t\t\t<MarkButton format=\"underline\" icon={Underline} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"code\") && (\n\t\t\t\t\t\t\t<MarkButton format=\"code\" icon={Code} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"left\") && (\n\t\t\t\t\t\t\t<BlockButton format=\"left\" icon={AlignLeft} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"right\") && (\n\t\t\t\t\t\t\t<BlockButton format=\"right\" icon={AlignRight} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"center\") && (\n\t\t\t\t\t\t\t<BlockButton format=\"center\" icon={AlignCenter} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"justify\") && (\n\t\t\t\t\t\t\t<BlockButton format=\"justify\" icon={AlignJustify} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{imageConfig && buttonIsNotHidden(\"image\") && (\n\t\t\t\t\t\t\t<InsertImage {...imageConfig} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"video\") && <InsertVideo {...videoConfig} />}\n\t\t\t\t\t</Toolbar>\n\n\t\t\t\t\t<Editable\n\t\t\t\t\t\tclassName=\"editorContainer\"\n\t\t\t\t\t\trenderElement={renderElement}\n\t\t\t\t\t\trenderLeaf={renderLeaf}\n\t\t\t\t\t\tspellCheck\n\t\t\t\t\t\tref={ref}\n\t\t\t\t\t\tid={inputId}\n\t\t\t\t\t\tonFocus={() => setOnFocus(true)}\n\t\t\t\t\t\tonBlur={() => setOnFocus(false)}\n\t\t\t\t\t\tonKeyDown={(event) => {\n\t\t\t\t\t\t\tfor (const hotkey in hotKeys) {\n\t\t\t\t\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: intentional\n\t\t\t\t\t\t\t\tif (isHotkey(hotkey, event as any)) {\n\t\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t\tconst mark = hotKeys[hotkey as keyof typeof hotKeys];\n\t\t\t\t\t\t\t\t\ttoggleMark(editor, mark);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\n\t\t\t\t\t{restatesCharacters < 0 && (\n\t\t\t\t\t\t<div className=\"restatesCharacters\">{restatesCharacters}</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<input\n\t\t\t\t\ttype=\"hidden\"\n\t\t\t\t\tname={name}\n\t\t\t\t\tvalue={inputValue.slice(0, maxLimit)}\n\t\t\t\t/>\n\n\t\t\t\t<input type=\"hidden\" name={`${name}Count`} value={charactersCount} />\n\t\t\t</Slate>\n\t\t</FieldTemplate>\n\t);\n}\n\nexport { RichText };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsFA,SAAS,EAAS,GAAsB;CACvC,IAAM,EACL,SACA,kBACA,gBACA,gBACA,WAAW,IAAmB,IAC9B,kBAAe,MACf,2BAAwB,IACxB,4BACA,qBACA,cAAW,KACX,aACA,SAAS,GACT,WACA,iBACA,OACA,iBAAc,YACd,yBAAsB,OACnB,GAEE,IAAS,QAAc,EAAY,EAAU,EAAa,CAAC,CAAC,GAAG,CAAC,CAAC,GAEjE,EAAE,oBAAgB,EAAQ,GAE1B,IAAe,QAA4B;EAChD,IAAI;GACH,IAAM,IAAc,KAAK,MAAM,CAAY;GAa3C,OAZI,CAAC,MAAM,QAAQ,CAAW,KAAK,EAAY,WAAW,IAClD,gBAAgB,CAAY,IAGf,EAAY,OAC/B,MACA,OAAO,KAAS,cAChB,KACA,UAAU,KACV,cAAc,CAGT,IAAe,IAAc,gBAAgB,CAAY;EACjE,QAAiB;GAChB,OAAO,gBAAgB,CAAY;EACpC;CACD,GAAG,CAAC,CAAY,CAAC,GAIX,CAAC,GAAiB,MAAsB,EAFxB,EAAoB,CAEa,CAAA,CAAc,MAAM,GACrE,CAAC,IAAY,MAAiB,EACnC,KAAK,UAAU,CAAY,KAAK,IACjC,GAEM,CAAC,IAAS,KAAc,EAAS,EAAK,GAEtC,KAAM,EAAyB,IAAI,GACnC,KAAc,GAAM,GACpB,KAAU,KAAM,IAEhB,IAAe,KAAoB,KAAc,IACjD,KAAU,KAAe,CAAC,CAAC,GAE3B,KAAa,EAAY,GAAM,CAAC,CAAC,GACjC,KAAgB,EAAY,GAAS,CAAC,CAAC;CAE7C,SAAS,EAAa,GAAqB;EAC1C,IAAM,IAAO,EAAoB,CAAK;EAEtC,GAAmB,EAAK,MAAM,GAC9B,IAA0B,EAAK,MAAM,GAEjC,OAAyB,EAAK,UAAU,OAI5C,GAAc,KAAK,UAAU,CAAK,CAAC,GACnC,IAAW,CAAK;CACjB;CASA,IAAM,KAAY,iBANC,MAEhB,IAAW,IADX,cAGC,aAE0C,GAP3B,KAAU,cAAc,gBASrC,IAAqB,IAAW;CAEtC,SAAS,EAAkB,GAAiC;EAC3D,OAAO,CAAC,GAAe,SAAS,CAAM;CACvC;CAEA,OACC,kBAAC,GAAD;EACO;EACC;EACO;EACd,WAAW;EACG;EACO;EACR;YAEb,kBAAC,GAAD;GACS;GACR,cAAc;GACd,UAAU;GACV,eAAe;aAJhB;IAMC,kBAAC,OAAD;KAAgB;eAAhB;MACC,kBAAC,IAAD,EAAA,UAAA;OACE,EAAkB,YAAY,KAC9B,kBAAC,GAAD;QAAa,QAAO;QAAa,MAAM;OAAW,CAAA;OAGlD,EAAkB,YAAY,KAC9B,kBAAC,GAAD;QAAa,QAAO;QAAa,MAAM;OAAW,CAAA;OAGlD,EAAkB,YAAY,KAC9B,kBAAC,GAAD;QAAa,QAAO;QAAa,MAAM;OAAQ,CAAA;OAG/C,EAAkB,MAAM,KACxB,kBAAC,GAAD;QAAY,QAAO;QAAO,MAAM;OAAO,CAAA;OAGvC,EAAkB,QAAQ,KAC1B,kBAAC,GAAD;QAAY,QAAO;QAAS,MAAM;OAAS,CAAA;OAG3C,EAAkB,WAAW,KAC7B,kBAAC,GAAD;QAAY,QAAO;QAAY,MAAM;OAAY,CAAA;OAGjD,EAAkB,MAAM,KACxB,kBAAC,GAAD;QAAY,QAAO;QAAO,MAAM;OAAO,CAAA;OAGvC,EAAkB,MAAM,KACxB,kBAAC,GAAD;QAAa,QAAO;QAAO,MAAM;OAAY,CAAA;OAG7C,EAAkB,OAAO,KACzB,kBAAC,GAAD;QAAa,QAAO;QAAQ,MAAM;OAAa,CAAA;OAG/C,EAAkB,QAAQ,KAC1B,kBAAC,GAAD;QAAa,QAAO;QAAS,MAAM;OAAc,CAAA;OAGjD,EAAkB,SAAS,KAC3B,kBAAC,GAAD;QAAa,QAAO;QAAU,MAAM;OAAe,CAAA;OAGnD,KAAe,EAAkB,OAAO,KACxC,kBAAC,GAAD,EAAa,GAAI,EAAc,CAAA;OAG/B,EAAkB,OAAO,KAAK,kBAAC,GAAD,EAAa,GAAI,EAAc,CAAA;MACtD,EAAA,CAAA;MAET,kBAAC,GAAD;OACC,WAAU;OACK;OACH;OACZ,YAAA;OACK;OACL,IAAI;OACJ,eAAe,EAAW,EAAI;OAC9B,cAAc,EAAW,EAAK;OAC9B,YAAY,MAAU;QACrB,KAAK,IAAM,KAAU,GAEpB,IAAI,EAAS,GAAQ,CAAY,GAAG;SACnC,EAAM,eAAe;SACrB,IAAM,IAAO,EAAQ;SACrB,EAAW,GAAQ,CAAI;QACxB;OAEF;MACA,CAAA;MAEA,IAAqB,KACrB,kBAAC,OAAD;OAAK,WAAU;iBAAsB;MAAwB,CAAA;KAE1D;;IAEL,kBAAC,SAAD;KACC,MAAK;KACC;KACN,OAAO,GAAW,MAAM,GAAG,CAAQ;IACnC,CAAA;IAED,kBAAC,SAAD;KAAO,MAAK;KAAS,MAAM,GAAG,EAAK;KAAQ,OAAO;IAAkB,CAAA;GAC9D;;CACO,CAAA;AAEjB"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/richText/index.tsx"],"sourcesContent":["import isHotkey from \"is-hotkey\";\nimport {\n\tAlignCenter,\n\tAlignJustify,\n\tAlignLeft,\n\tAlignRight,\n\tBold,\n\tCode,\n\tHeading1,\n\tHeading2,\n\tItalic,\n\tQuote,\n\tUnderline,\n} from \"lucide-react\";\nimport { useCallback, useId, useMemo, useRef, useState } from \"react\";\nimport { createEditor, type Descendant } from \"slate\";\nimport { withHistory } from \"slate-history\";\nimport { Editable, Slate, withReact } from \"slate-react\";\nimport { useForm } from \"../../hooks/useForm\";\nimport { extractTextFromNode } from \"../../services/extractTextFromNode\";\nimport { FieldTemplate } from \"../../services/fieldTemplate\";\nimport { toggleMark } from \"../../services/toggleMark\";\nimport { hotKeys, initialValue } from \"../../templates/richTextTemplates\";\nimport type {\n\tRichTextHiddenButtonKey,\n\tRichTextProps,\n} from \"../../types/richTextTypes\";\nimport { BlockButton } from \"./blockButton\";\nimport { Element } from \"./element\";\nimport { InsertImage } from \"./insertImage\";\nimport { InsertLink } from \"./insertLink\";\nimport { InsertVideo } from \"./insertVideo\";\nimport { Leaf } from \"./leaf\";\nimport { MarkButton } from \"./markButton\";\nimport { Toolbar } from \"./toolbar\";\nimport \"./styles.css\";\n\n/**\n * RichText, WYSIWYG rich-text editor built on Slate.js with a configurable toolbar.\n *\n * **Toolbar features:** bold, italic, underline, code, H1/H2, block quote, alignment (left/center/right/justify), image, video, and link insertion.\n *\n * Editor content is stored as a Slate JSON string in a hidden `<input>` for form submission.\n * Integrates with `useForm` to display validation errors by field name.\n *\n * @param props.name - Field name for form submission. Required.\n * @param props.label - Label text displayed above the editor.\n * @param props.hiddenButtons - Toolbar button keys to hide (e.g. `[\"image\", \"code\"]`).\n * @param props.imageConfig - Enables image insertion; contains the upload endpoint and modal labels.\n * @param props.videoConfig - Customizes the video insertion modal labels.\n * @param props.linkConfig - Customizes the link insertion modal labels.\n * @param props.defaultValue - Initial editor content as a Slate JSON string. Default: \"[]\"\n * @param props.maxLimit - Maximum character count. Default: 10000\n * @param props.enforceCharacterLimit - Prevents typing past `maxLimit`. Default: false\n * @param props.onChangeCharactersCount - Callback fired on every keystroke, receives the current character count.\n * @param props.onChange - Callback fired when editor content changes, receives the Slate `Descendant[]`.\n * @param props.baseErrorMessage - Custom error message (overrides `useForm` context error).\n * @param props.isError - Forces the error visual state.\n * @param props.id - Custom id for the editable area element.\n * @param props.showAsterisk - Appends `*` to the label.\n * @param props.orientation - Layout direction. Default: \"horizontal\"\n * @param props.unShowFieldTemplate - Skips wrapper, label, and error rendering. Default: false\n *\n * @returns RichText JSX element wrapped in `FieldTemplate`.\n *\n * @example\n * ```tsx\n * // Basic\n * <RichText name=\"content\" />\n *\n * // With character limit and hidden toolbar buttons\n * <RichText\n * name=\"description\"\n * label=\"Description\"\n * maxLimit={500}\n * enforceCharacterLimit\n * hiddenButtons={[\"image\", \"video\", \"code\"]}\n * onChangeCharactersCount={(n) => setCharCount(n)}\n * />\n *\n * // With image upload support\n * <RichText\n * name=\"article\"\n * label=\"Article Body\"\n * imageConfig={{ action: \"/api/upload\" }}\n * />\n *\n * // With custom link insertion modal labels\n * <RichText\n * name=\"article\"\n * label=\"Article Body\"\n * linkConfig={{ modalTitle: \"Add a link\" }}\n * />\n * ```\n */\n\nfunction RichText(props: RichTextProps) {\n\tconst {\n\t\tname,\n\t\thiddenButtons,\n\t\timageConfig,\n\t\tvideoConfig,\n\t\tlinkConfig,\n\t\tclassName: wrapperClassName = \"\",\n\t\tdefaultValue = \"[]\",\n\t\tenforceCharacterLimit = false,\n\t\tonChangeCharactersCount,\n\t\tbaseErrorMessage,\n\t\tmaxLimit = 10000,\n\t\tonChange,\n\t\tisError: baseIsError,\n\t\tlabel,\n\t\tshowAsterisk,\n\t\tid,\n\t\torientation = \"vertical\",\n\t\tunShowFieldTemplate = false,\n\t} = props;\n\n\tconst editor = useMemo(() => withHistory(withReact(createEditor())), []);\n\n\tconst { fieldErrors } = useForm();\n\n\tconst defaultNodes = useMemo((): Descendant[] => {\n\t\ttry {\n\t\t\tconst parsedNodes = JSON.parse(defaultValue);\n\t\t\tif (!Array.isArray(parsedNodes) || parsedNodes.length === 0) {\n\t\t\t\treturn structuredClone(initialValue);\n\t\t\t}\n\n\t\t\tconst isValidNodes = parsedNodes.every(\n\t\t\t\t(node) =>\n\t\t\t\t\ttypeof node === \"object\" &&\n\t\t\t\t\tnode !== null &&\n\t\t\t\t\t\"type\" in node &&\n\t\t\t\t\t\"children\" in node,\n\t\t\t);\n\n\t\t\treturn isValidNodes ? parsedNodes : structuredClone(initialValue);\n\t\t} catch (_error) {\n\t\t\treturn structuredClone(initialValue);\n\t\t}\n\t}, [defaultValue]);\n\n\tconst textFromNodes = extractTextFromNode(defaultNodes);\n\n\tconst [charactersCount, setCharactersCount] = useState(textFromNodes.length);\n\tconst [inputValue, setInputValue] = useState(\n\t\tJSON.stringify(defaultNodes) || \"[]\",\n\t);\n\n\tconst [onFocus, setOnFocus] = useState(false);\n\n\tconst ref = useRef<HTMLInputElement>(null);\n\tconst generatedId = useId();\n\tconst inputId = id || generatedId;\n\n\tconst errorMessage = baseErrorMessage || fieldErrors?.[name];\n\tconst isError = baseIsError || !!errorMessage;\n\n\tconst renderLeaf = useCallback(Leaf, []);\n\tconst renderElement = useCallback(Element, []);\n\n\tfunction handleChange(value: Descendant[]) {\n\t\tconst text = extractTextFromNode(value);\n\n\t\tsetCharactersCount(text.length);\n\t\tonChangeCharactersCount?.(text.length);\n\n\t\tif (enforceCharacterLimit && text.length >= maxLimit) {\n\t\t\treturn;\n\t\t}\n\n\t\tsetInputValue(JSON.stringify(value));\n\t\tonChange?.(value);\n\t}\n\n\tconst focusClass = onFocus ? \"focusTrue\" : \"focusFalse\";\n\tconst errorClass = isError\n\t\t? \"errorTrue\"\n\t\t: maxLimit < charactersCount\n\t\t\t? \"errorTrue\"\n\t\t\t: \"errorFalse\";\n\n\tconst className = `arkynRichText ${errorClass} ${focusClass}`;\n\n\tconst restatesCharacters = maxLimit - charactersCount;\n\n\tfunction buttonIsNotHidden(format: RichTextHiddenButtonKey) {\n\t\treturn !hiddenButtons?.includes(format);\n\t}\n\n\treturn (\n\t\t<FieldTemplate\n\t\t\tname={name}\n\t\t\tlabel={label}\n\t\t\tshowAsterisk={showAsterisk}\n\t\t\tclassName={wrapperClassName}\n\t\t\terrorMessage={errorMessage}\n\t\t\tunShowFieldTemplate={unShowFieldTemplate}\n\t\t\torientation={orientation}\n\t\t>\n\t\t\t<Slate\n\t\t\t\teditor={editor}\n\t\t\t\tinitialValue={defaultNodes}\n\t\t\t\tonChange={handleChange}\n\t\t\t\tonValueChange={handleChange}\n\t\t\t>\n\t\t\t\t<div className={className}>\n\t\t\t\t\t<Toolbar>\n\t\t\t\t\t\t{buttonIsNotHidden(\"headingOne\") && (\n\t\t\t\t\t\t\t<BlockButton format=\"headingOne\" icon={Heading1} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"headingTwo\") && (\n\t\t\t\t\t\t\t<BlockButton format=\"headingTwo\" icon={Heading2} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"blockQuote\") && (\n\t\t\t\t\t\t\t<BlockButton format=\"blockQuote\" icon={Quote} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"bold\") && (\n\t\t\t\t\t\t\t<MarkButton format=\"bold\" icon={Bold} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"italic\") && (\n\t\t\t\t\t\t\t<MarkButton format=\"italic\" icon={Italic} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"underline\") && (\n\t\t\t\t\t\t\t<MarkButton format=\"underline\" icon={Underline} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"code\") && (\n\t\t\t\t\t\t\t<MarkButton format=\"code\" icon={Code} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"left\") && (\n\t\t\t\t\t\t\t<BlockButton format=\"left\" icon={AlignLeft} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"right\") && (\n\t\t\t\t\t\t\t<BlockButton format=\"right\" icon={AlignRight} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"center\") && (\n\t\t\t\t\t\t\t<BlockButton format=\"center\" icon={AlignCenter} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"justify\") && (\n\t\t\t\t\t\t\t<BlockButton format=\"justify\" icon={AlignJustify} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{imageConfig && buttonIsNotHidden(\"image\") && (\n\t\t\t\t\t\t\t<InsertImage {...imageConfig} />\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"video\") && <InsertVideo {...videoConfig} />}\n\n\t\t\t\t\t\t{buttonIsNotHidden(\"link\") && <InsertLink {...linkConfig} />}\n\t\t\t\t\t</Toolbar>\n\n\t\t\t\t\t<Editable\n\t\t\t\t\t\tclassName=\"editorContainer\"\n\t\t\t\t\t\trenderElement={renderElement}\n\t\t\t\t\t\trenderLeaf={renderLeaf}\n\t\t\t\t\t\tspellCheck\n\t\t\t\t\t\tref={ref}\n\t\t\t\t\t\tid={inputId}\n\t\t\t\t\t\tonFocus={() => setOnFocus(true)}\n\t\t\t\t\t\tonBlur={() => setOnFocus(false)}\n\t\t\t\t\t\tonKeyDown={(event) => {\n\t\t\t\t\t\t\tfor (const hotkey in hotKeys) {\n\t\t\t\t\t\t\t\t// biome-ignore lint/suspicious/noExplicitAny: intentional\n\t\t\t\t\t\t\t\tif (isHotkey(hotkey, event as any)) {\n\t\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t\tconst mark = hotKeys[hotkey as keyof typeof hotKeys];\n\t\t\t\t\t\t\t\t\ttoggleMark(editor, mark);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\n\t\t\t\t\t{restatesCharacters < 0 && (\n\t\t\t\t\t\t<div className=\"restatesCharacters\">{restatesCharacters}</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<input\n\t\t\t\t\ttype=\"hidden\"\n\t\t\t\t\tname={name}\n\t\t\t\t\tvalue={inputValue.slice(0, maxLimit)}\n\t\t\t\t/>\n\n\t\t\t\t<input type=\"hidden\" name={`${name}Count`} value={charactersCount} />\n\t\t\t</Slate>\n\t\t</FieldTemplate>\n\t);\n}\n\nexport { RichText };\n"],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Button as e } from "../../button/index.js";
|
|
2
|
+
import { Input as t } from "../../input/index.js";
|
|
3
|
+
import { ModalContainer as n } from "../../modal/modalContainer/index.js";
|
|
4
|
+
import { ModalFooter as r } from "../../modal/modalFooter/index.js";
|
|
5
|
+
import { ModalHeader as i } from "../../modal/modalHeader/index.js";
|
|
6
|
+
import { isValidHttpsUrl as a } from "../../../services/isValidHttpsUrl.js";
|
|
7
|
+
/* empty css */
|
|
8
|
+
import { useEffect as o, useState as s } from "react";
|
|
9
|
+
import { Fragment as c, jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
10
|
+
import { Link as d } from "lucide-react";
|
|
11
|
+
import { Editor as f, Range as p, Transforms as m } from "slate";
|
|
12
|
+
import { useSlate as h } from "slate-react";
|
|
13
|
+
//#region src/components/richText/insertLink/index.tsx
|
|
14
|
+
function g(g) {
|
|
15
|
+
let { modalCancelButton: _ = "Cancelar", modalConfirmButton: v = "Confirmar", modalInputUrlLabel: y = "URL do link:", modalTitle: b = "Inserir link", invalidUrlMessage: x = "URL inválida" } = g, S = h(), [C, w] = s(!1), [T, E] = s(""), [D, O] = s(S.selection), k = a(T), A = T && !k ? x : "";
|
|
16
|
+
function j(e) {
|
|
17
|
+
e.preventDefault(), O(S.selection), w(!0);
|
|
18
|
+
}
|
|
19
|
+
function M(e) {
|
|
20
|
+
e.preventDefault(), !(!k || !D) && (m.select(S, D), p.isCollapsed(D) ? m.insertNodes(S, {
|
|
21
|
+
text: T,
|
|
22
|
+
link: !0,
|
|
23
|
+
href: T
|
|
24
|
+
}) : (f.addMark(S, "link", !0), f.addMark(S, "href", T)), w(!1));
|
|
25
|
+
}
|
|
26
|
+
return o(() => {
|
|
27
|
+
C || (E(""), O(null));
|
|
28
|
+
}, [C]), /* @__PURE__ */ u(c, { children: [/* @__PURE__ */ l("button", {
|
|
29
|
+
type: "button",
|
|
30
|
+
className: "arkynRichTextInsertLink",
|
|
31
|
+
onMouseDown: j,
|
|
32
|
+
children: /* @__PURE__ */ l(d, {})
|
|
33
|
+
}), /* @__PURE__ */ u(n, {
|
|
34
|
+
isVisible: C,
|
|
35
|
+
makeInvisible: () => w(!1),
|
|
36
|
+
children: [
|
|
37
|
+
/* @__PURE__ */ l(i, { children: b }),
|
|
38
|
+
/* @__PURE__ */ l("div", {
|
|
39
|
+
className: "arkynRichTextInsertLinkModalContent",
|
|
40
|
+
children: /* @__PURE__ */ l(t, {
|
|
41
|
+
type: "text",
|
|
42
|
+
name: "richTextLinkURL",
|
|
43
|
+
label: y,
|
|
44
|
+
showAsterisk: !0,
|
|
45
|
+
defaultValue: T,
|
|
46
|
+
onChange: (e) => E(e.target.value),
|
|
47
|
+
errorMessage: A
|
|
48
|
+
})
|
|
49
|
+
}),
|
|
50
|
+
/* @__PURE__ */ u(r, { children: [/* @__PURE__ */ l(e, {
|
|
51
|
+
type: "button",
|
|
52
|
+
scheme: "danger",
|
|
53
|
+
variant: "outline",
|
|
54
|
+
onClick: () => w(!1),
|
|
55
|
+
children: _
|
|
56
|
+
}), /* @__PURE__ */ l(e, {
|
|
57
|
+
disabled: !k,
|
|
58
|
+
type: "button",
|
|
59
|
+
onClick: M,
|
|
60
|
+
children: v
|
|
61
|
+
})] })
|
|
62
|
+
]
|
|
63
|
+
})] });
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
export { g as InsertLink };
|
|
67
|
+
|
|
68
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../../src/components/richText/insertLink/index.tsx"],"sourcesContent":["import { Link } from \"lucide-react\";\nimport { type MouseEvent, useEffect, useState } from \"react\";\nimport { Editor, Range, Transforms } from \"slate\";\nimport { useSlate } from \"slate-react\";\n\nimport { isValidHttpsUrl } from \"../../../services/isValidHttpsUrl\";\nimport type { RichTextInsertLinkProps } from \"../../../types/richTextTypes\";\n\nimport { Button } from \"../../button\";\nimport { Input } from \"../../input\";\nimport { ModalContainer } from \"../../modal/modalContainer\";\nimport { ModalFooter } from \"../../modal/modalFooter\";\nimport { ModalHeader } from \"../../modal/modalHeader\";\n\nimport \"./styles.css\";\n\nfunction InsertLink(props: RichTextInsertLinkProps) {\n\tconst {\n\t\tmodalCancelButton = \"Cancelar\",\n\t\tmodalConfirmButton = \"Confirmar\",\n\t\tmodalInputUrlLabel = \"URL do link:\",\n\t\tmodalTitle = \"Inserir link\",\n\t\tinvalidUrlMessage = \"URL inválida\",\n\t} = props;\n\n\tconst editor = useSlate();\n\n\tconst [modalIsVisible, setModalIsVisible] = useState(false);\n\tconst [linkURL, setLinkURL] = useState(\"\");\n\tconst [selection, setSelection] = useState(editor.selection);\n\n\tconst isValidUrl = isValidHttpsUrl(linkURL);\n\tconst errorMessage = linkURL && !isValidUrl ? invalidUrlMessage : \"\";\n\n\tfunction handleOpenModal(event: MouseEvent<HTMLButtonElement>) {\n\t\tevent.preventDefault();\n\t\tsetSelection(editor.selection);\n\t\tsetModalIsVisible(true);\n\t}\n\n\tfunction handleConfirm(event: MouseEvent<HTMLButtonElement>) {\n\t\tevent.preventDefault();\n\t\tif (!isValidUrl || !selection) return;\n\n\t\tTransforms.select(editor, selection);\n\n\t\tif (Range.isCollapsed(selection)) {\n\t\t\tTransforms.insertNodes(editor, {\n\t\t\t\ttext: linkURL,\n\t\t\t\tlink: true,\n\t\t\t\thref: linkURL,\n\t\t\t});\n\t\t} else {\n\t\t\tEditor.addMark(editor, \"link\", true);\n\t\t\tEditor.addMark(editor, \"href\", linkURL);\n\t\t}\n\n\t\tsetModalIsVisible(false);\n\t}\n\n\tuseEffect(() => {\n\t\tif (!modalIsVisible) {\n\t\t\tsetLinkURL(\"\");\n\t\t\tsetSelection(null);\n\t\t}\n\t}, [modalIsVisible]);\n\n\treturn (\n\t\t<>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tclassName=\"arkynRichTextInsertLink\"\n\t\t\t\tonMouseDown={handleOpenModal}\n\t\t\t>\n\t\t\t\t<Link />\n\t\t\t</button>\n\n\t\t\t<ModalContainer\n\t\t\t\tisVisible={modalIsVisible}\n\t\t\t\tmakeInvisible={() => setModalIsVisible(false)}\n\t\t\t>\n\t\t\t\t<ModalHeader>{modalTitle}</ModalHeader>\n\n\t\t\t\t<div className=\"arkynRichTextInsertLinkModalContent\">\n\t\t\t\t\t<Input\n\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\tname=\"richTextLinkURL\"\n\t\t\t\t\t\tlabel={modalInputUrlLabel}\n\t\t\t\t\t\tshowAsterisk\n\t\t\t\t\t\tdefaultValue={linkURL}\n\t\t\t\t\t\tonChange={(e) => setLinkURL(e.target.value)}\n\t\t\t\t\t\terrorMessage={errorMessage}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\n\t\t\t\t<ModalFooter>\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tscheme=\"danger\"\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tonClick={() => setModalIsVisible(false)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{modalCancelButton}\n\t\t\t\t\t</Button>\n\n\t\t\t\t\t<Button disabled={!isValidUrl} type=\"button\" onClick={handleConfirm}>\n\t\t\t\t\t\t{modalConfirmButton}\n\t\t\t\t\t</Button>\n\t\t\t\t</ModalFooter>\n\t\t\t</ModalContainer>\n\t\t</>\n\t);\n}\n\nexport { InsertLink };\n"],"mappings":";;;;;;;;;;;;;AAgBA,SAAS,EAAW,GAAgC;CACnD,IAAM,EACL,uBAAoB,YACpB,wBAAqB,aACrB,wBAAqB,gBACrB,gBAAa,gBACb,uBAAoB,mBACjB,GAEE,IAAS,EAAS,GAElB,CAAC,GAAgB,KAAqB,EAAS,EAAK,GACpD,CAAC,GAAS,KAAc,EAAS,EAAE,GACnC,CAAC,GAAW,KAAgB,EAAS,EAAO,SAAS,GAErD,IAAa,EAAgB,CAAO,GACpC,IAAe,KAAW,CAAC,IAAa,IAAoB;CAElE,SAAS,EAAgB,GAAsC;EAG9D,AAFA,EAAM,eAAe,GACrB,EAAa,EAAO,SAAS,GAC7B,EAAkB,EAAI;CACvB;CAEA,SAAS,EAAc,GAAsC;EAC5D,EAAM,eAAe,GACjB,GAAC,KAAc,CAAC,OAEpB,EAAW,OAAO,GAAQ,CAAS,GAE/B,EAAM,YAAY,CAAS,IAC9B,EAAW,YAAY,GAAQ;GAC9B,MAAM;GACN,MAAM;GACN,MAAM;EACP,CAAC,KAED,EAAO,QAAQ,GAAQ,QAAQ,EAAI,GACnC,EAAO,QAAQ,GAAQ,QAAQ,CAAO,IAGvC,EAAkB,EAAK;CACxB;CASA,OAPA,QAAgB;EACf,AAAK,MACJ,EAAW,EAAE,GACb,EAAa,IAAI;CAEnB,GAAG,CAAC,CAAc,CAAC,GAGlB,kBAAA,GAAA,EAAA,UAAA,CACC,kBAAC,UAAD;EACC,MAAK;EACL,WAAU;EACV,aAAa;YAEb,kBAAC,GAAD,CAAO,CAAA;CACA,CAAA,GAER,kBAAC,GAAD;EACC,WAAW;EACX,qBAAqB,EAAkB,EAAK;YAF7C;GAIC,kBAAC,GAAD,EAAA,UAAc,EAAwB,CAAA;GAEtC,kBAAC,OAAD;IAAK,WAAU;cACd,kBAAC,GAAD;KACC,MAAK;KACL,MAAK;KACL,OAAO;KACP,cAAA;KACA,cAAc;KACd,WAAW,MAAM,EAAW,EAAE,OAAO,KAAK;KAC5B;IACd,CAAA;GACG,CAAA;GAEL,kBAAC,GAAD,EAAA,UAAA,CACC,kBAAC,GAAD;IACC,MAAK;IACL,QAAO;IACP,SAAQ;IACR,eAAe,EAAkB,EAAK;cAErC;GACM,CAAA,GAER,kBAAC,GAAD;IAAQ,UAAU,CAAC;IAAY,MAAK;IAAS,SAAS;cACpD;GACM,CAAA,CACI,EAAA,CAAA;EACE;GACf,EAAA,CAAA;AAEJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer arkyn{.arkynRichTextInsertLink{background-color:var(--background-underground,#fafafa);border:none;border-radius:4px;justify-content:center;align-items:center;width:36px;height:36px;display:flex}.arkynRichTextInsertLink svg{width:20px;height:20px;color:var(--text-heading,#09090b)}.arkynRichTextInsertLink:hover{cursor:pointer;filter:brightness(.98)}.arkynRichTextInsertLinkModalContent{flex-direction:column;gap:16px;width:500px;padding:16px;display:flex}html.dark .arkynRichTextInsertLink:hover{cursor:pointer;filter:brightness(.9)}}
|
|
@@ -3,18 +3,19 @@ import { Input as t } from "../../input/index.js";
|
|
|
3
3
|
import { ModalContainer as n } from "../../modal/modalContainer/index.js";
|
|
4
4
|
import { ModalFooter as r } from "../../modal/modalFooter/index.js";
|
|
5
5
|
import { ModalHeader as i } from "../../modal/modalHeader/index.js";
|
|
6
|
+
import { isValidHttpsUrl as a } from "../../../services/isValidHttpsUrl.js";
|
|
6
7
|
/* empty css */
|
|
7
|
-
import { useEffect as
|
|
8
|
-
import { Fragment as
|
|
9
|
-
import { Video as
|
|
10
|
-
import { useSlate as
|
|
8
|
+
import { useEffect as o, useState as s } from "react";
|
|
9
|
+
import { Fragment as c, jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
10
|
+
import { Video as d } from "lucide-react";
|
|
11
|
+
import { useSlate as f } from "slate-react";
|
|
11
12
|
//#region src/components/richText/insertVideo/index.tsx
|
|
12
|
-
function
|
|
13
|
-
let { modalCancelButton:
|
|
14
|
-
function
|
|
15
|
-
if (e.preventDefault(), !
|
|
16
|
-
let t = `https://www.youtube.com/embed/${
|
|
17
|
-
|
|
13
|
+
function p(p) {
|
|
14
|
+
let { modalCancelButton: m = "Cancelar", modalConfirmButton: h = "Confirmar", modalInputUrlLabel: g = "URL do vídeo:", modalTitle: _ = "Inserir vídeo", invalidUrlMessage: v = "URL inválida" } = p, y = f(), [b, x] = s(!1), [S, C] = s(""), [w, T] = s(""), [E, D] = s("");
|
|
15
|
+
function O(e) {
|
|
16
|
+
if (e.preventDefault(), !S || S === "") return;
|
|
17
|
+
let t = `https://www.youtube.com/embed/${S}`;
|
|
18
|
+
y.insertNodes([
|
|
18
19
|
{
|
|
19
20
|
type: "paragraph",
|
|
20
21
|
children: [{ text: "" }]
|
|
@@ -28,61 +29,64 @@ function f(f) {
|
|
|
28
29
|
type: "paragraph",
|
|
29
30
|
children: [{ text: "" }]
|
|
30
31
|
}
|
|
31
|
-
]),
|
|
32
|
+
]), x(!1);
|
|
32
33
|
}
|
|
33
|
-
return
|
|
34
|
-
if (!
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
return o(() => {
|
|
35
|
+
if (!w || w === "") return;
|
|
36
|
+
if (C(""), D(""), !a(w)) {
|
|
37
|
+
D(v);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
let e = new URLSearchParams(w.split("?")[1]).get("v");
|
|
41
|
+
e ? C(e) : D(v);
|
|
42
|
+
}, [w, v]), o(() => {
|
|
43
|
+
b || (C(""), T(""), D(""));
|
|
44
|
+
}, [b]), /* @__PURE__ */ u(c, { children: [/* @__PURE__ */ l("button", {
|
|
41
45
|
type: "button",
|
|
42
46
|
className: "arkynRichTextInsertVideo",
|
|
43
|
-
onMouseDown: () =>
|
|
44
|
-
children: /* @__PURE__ */
|
|
45
|
-
}), /* @__PURE__ */
|
|
46
|
-
isVisible:
|
|
47
|
-
makeInvisible: () =>
|
|
47
|
+
onMouseDown: () => x(!0),
|
|
48
|
+
children: /* @__PURE__ */ l(d, {})
|
|
49
|
+
}), /* @__PURE__ */ u(n, {
|
|
50
|
+
isVisible: b,
|
|
51
|
+
makeInvisible: () => x(!1),
|
|
48
52
|
children: [
|
|
49
|
-
/* @__PURE__ */
|
|
50
|
-
/* @__PURE__ */
|
|
53
|
+
/* @__PURE__ */ l(i, { children: _ }),
|
|
54
|
+
/* @__PURE__ */ u("div", {
|
|
51
55
|
className: "arkynRichTextInsertVideoModalContent",
|
|
52
|
-
children: [/* @__PURE__ */
|
|
56
|
+
children: [/* @__PURE__ */ l(t, {
|
|
53
57
|
type: "text",
|
|
54
58
|
name: "richTextVideoURL",
|
|
55
|
-
label:
|
|
59
|
+
label: g,
|
|
56
60
|
showAsterisk: !0,
|
|
57
|
-
defaultValue:
|
|
58
|
-
onChange: (e) =>
|
|
59
|
-
errorMessage:
|
|
60
|
-
}),
|
|
61
|
+
defaultValue: w,
|
|
62
|
+
onChange: (e) => T(e.target.value),
|
|
63
|
+
errorMessage: E
|
|
64
|
+
}), S && /* @__PURE__ */ l("iframe", {
|
|
61
65
|
width: "auto",
|
|
62
66
|
height: "315",
|
|
63
|
-
src: `https://www.youtube.com/embed/${
|
|
67
|
+
src: `https://www.youtube.com/embed/${S}`,
|
|
64
68
|
title: "YouTube video player",
|
|
65
69
|
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",
|
|
66
70
|
referrerPolicy: "strict-origin-when-cross-origin",
|
|
67
71
|
allowFullScreen: !0
|
|
68
72
|
})]
|
|
69
73
|
}),
|
|
70
|
-
/* @__PURE__ */
|
|
74
|
+
/* @__PURE__ */ u(r, { children: [/* @__PURE__ */ l(e, {
|
|
71
75
|
type: "button",
|
|
72
76
|
scheme: "danger",
|
|
73
77
|
variant: "outline",
|
|
74
|
-
onClick: () =>
|
|
75
|
-
children: p
|
|
76
|
-
}), /* @__PURE__ */ c(e, {
|
|
77
|
-
disabled: !x,
|
|
78
|
-
type: "button",
|
|
79
|
-
onClick: D,
|
|
78
|
+
onClick: () => x(!1),
|
|
80
79
|
children: m
|
|
80
|
+
}), /* @__PURE__ */ l(e, {
|
|
81
|
+
disabled: !S,
|
|
82
|
+
type: "button",
|
|
83
|
+
onClick: O,
|
|
84
|
+
children: h
|
|
81
85
|
})] })
|
|
82
86
|
]
|
|
83
87
|
})] });
|
|
84
88
|
}
|
|
85
89
|
//#endregion
|
|
86
|
-
export {
|
|
90
|
+
export { p as InsertVideo };
|
|
87
91
|
|
|
88
92
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../../src/components/richText/insertVideo/index.tsx"],"sourcesContent":["import { Video } from \"lucide-react\";\nimport { type MouseEvent, useEffect, useState } from \"react\";\nimport { useSlate } from \"slate-react\";\n\nimport type { RichTextInsertVideoProps } from \"../../../types/richTextTypes\";\n\nimport { Button } from \"../../button\";\nimport { Input } from \"../../input\";\nimport { ModalContainer } from \"../../modal/modalContainer\";\nimport { ModalFooter } from \"../../modal/modalFooter\";\nimport { ModalHeader } from \"../../modal/modalHeader\";\n\nimport \"./styles.css\";\n\nfunction InsertVideo(props: RichTextInsertVideoProps) {\n\tconst {\n\t\tmodalCancelButton = \"Cancelar\",\n\t\tmodalConfirmButton = \"Confirmar\",\n\t\tmodalInputUrlLabel = \"URL do vídeo:\",\n\t\tmodalTitle = \"Inserir vídeo\",\n\t\tinvalidUrlMessage = \"URL inválida\",\n\t} = props;\n\n\tconst editor = useSlate();\n\n\tconst [modalIsVisible, setModalIsVisible] = useState(false);\n\tconst [videoId, setVideoId] = useState(\"\");\n\tconst [videoRawURL, setVideoRawURL] = useState(\"\");\n\tconst [errorMessage, setErrorMessage] = useState(\"\");\n\n\tfunction handleMouseDown(event: MouseEvent<HTMLButtonElement>) {\n\t\tevent.preventDefault();\n\n\t\tif (!videoId || videoId === \"\") return;\n\t\tconst videoUrl = `https://www.youtube.com/embed/${videoId}`;\n\n\t\teditor.insertNodes([\n\t\t\t{ type: \"paragraph\", children: [{ text: \"\" }] },\n\t\t\t{ type: \"video\", src: videoUrl, children: [{ text: \"\" }] },\n\t\t\t{ type: \"paragraph\", children: [{ text: \"\" }] },\n\t\t]);\n\n\t\tsetModalIsVisible(false);\n\t}\n\n\tuseEffect(() => {\n\t\tif (!videoRawURL || videoRawURL === \"\") return;\n\n\t\tsetVideoId(\"\");\n\t\tsetErrorMessage(\"\");\n\n\t\tconst searchParams = new URLSearchParams(videoRawURL.split(\"?\")[1]);\n\t\tconst vParam = searchParams.get(\"v\");\n\t\tif (vParam) setVideoId(vParam);\n\t\telse setErrorMessage(invalidUrlMessage);\n\t}, [videoRawURL, invalidUrlMessage]);\n\n\tuseEffect(() => {\n\t\tif (!modalIsVisible) {\n\t\t\tsetVideoId(\"\");\n\t\t\tsetVideoRawURL(\"\");\n\t\t\tsetErrorMessage(\"\");\n\t\t}\n\t}, [modalIsVisible]);\n\n\treturn (\n\t\t<>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tclassName=\"arkynRichTextInsertVideo\"\n\t\t\t\tonMouseDown={() => setModalIsVisible(true)}\n\t\t\t>\n\t\t\t\t<Video />\n\t\t\t</button>\n\n\t\t\t<ModalContainer\n\t\t\t\tisVisible={modalIsVisible}\n\t\t\t\tmakeInvisible={() => setModalIsVisible(false)}\n\t\t\t>\n\t\t\t\t<ModalHeader>{modalTitle}</ModalHeader>\n\n\t\t\t\t<div className=\"arkynRichTextInsertVideoModalContent\">\n\t\t\t\t\t<Input\n\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\tname=\"richTextVideoURL\"\n\t\t\t\t\t\tlabel={modalInputUrlLabel}\n\t\t\t\t\t\tshowAsterisk\n\t\t\t\t\t\tdefaultValue={videoRawURL}\n\t\t\t\t\t\tonChange={(e) => setVideoRawURL(e.target.value)}\n\t\t\t\t\t\terrorMessage={errorMessage}\n\t\t\t\t\t/>\n\n\t\t\t\t\t{videoId && (\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\twidth=\"auto\"\n\t\t\t\t\t\t\theight=\"315\"\n\t\t\t\t\t\t\tsrc={`https://www.youtube.com/embed/${videoId}`}\n\t\t\t\t\t\t\ttitle=\"YouTube video player\"\n\t\t\t\t\t\t\tallow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n\t\t\t\t\t\t\treferrerPolicy=\"strict-origin-when-cross-origin\"\n\t\t\t\t\t\t\tallowFullScreen\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<ModalFooter>\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tscheme=\"danger\"\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tonClick={() => setModalIsVisible(false)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{modalCancelButton}\n\t\t\t\t\t</Button>\n\n\t\t\t\t\t<Button disabled={!videoId} type=\"button\" onClick={handleMouseDown}>\n\t\t\t\t\t\t{modalConfirmButton}\n\t\t\t\t\t</Button>\n\t\t\t\t</ModalFooter>\n\t\t\t</ModalContainer>\n\t\t</>\n\t);\n}\n\nexport { InsertVideo };\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../../src/components/richText/insertVideo/index.tsx"],"sourcesContent":["import { Video } from \"lucide-react\";\nimport { type MouseEvent, useEffect, useState } from \"react\";\nimport { useSlate } from \"slate-react\";\n\nimport { isValidHttpsUrl } from \"../../../services/isValidHttpsUrl\";\nimport type { RichTextInsertVideoProps } from \"../../../types/richTextTypes\";\n\nimport { Button } from \"../../button\";\nimport { Input } from \"../../input\";\nimport { ModalContainer } from \"../../modal/modalContainer\";\nimport { ModalFooter } from \"../../modal/modalFooter\";\nimport { ModalHeader } from \"../../modal/modalHeader\";\n\nimport \"./styles.css\";\n\nfunction InsertVideo(props: RichTextInsertVideoProps) {\n\tconst {\n\t\tmodalCancelButton = \"Cancelar\",\n\t\tmodalConfirmButton = \"Confirmar\",\n\t\tmodalInputUrlLabel = \"URL do vídeo:\",\n\t\tmodalTitle = \"Inserir vídeo\",\n\t\tinvalidUrlMessage = \"URL inválida\",\n\t} = props;\n\n\tconst editor = useSlate();\n\n\tconst [modalIsVisible, setModalIsVisible] = useState(false);\n\tconst [videoId, setVideoId] = useState(\"\");\n\tconst [videoRawURL, setVideoRawURL] = useState(\"\");\n\tconst [errorMessage, setErrorMessage] = useState(\"\");\n\n\tfunction handleMouseDown(event: MouseEvent<HTMLButtonElement>) {\n\t\tevent.preventDefault();\n\n\t\tif (!videoId || videoId === \"\") return;\n\t\tconst videoUrl = `https://www.youtube.com/embed/${videoId}`;\n\n\t\teditor.insertNodes([\n\t\t\t{ type: \"paragraph\", children: [{ text: \"\" }] },\n\t\t\t{ type: \"video\", src: videoUrl, children: [{ text: \"\" }] },\n\t\t\t{ type: \"paragraph\", children: [{ text: \"\" }] },\n\t\t]);\n\n\t\tsetModalIsVisible(false);\n\t}\n\n\tuseEffect(() => {\n\t\tif (!videoRawURL || videoRawURL === \"\") return;\n\n\t\tsetVideoId(\"\");\n\t\tsetErrorMessage(\"\");\n\n\t\tif (!isValidHttpsUrl(videoRawURL)) {\n\t\t\tsetErrorMessage(invalidUrlMessage);\n\t\t\treturn;\n\t\t}\n\n\t\tconst searchParams = new URLSearchParams(videoRawURL.split(\"?\")[1]);\n\t\tconst vParam = searchParams.get(\"v\");\n\t\tif (vParam) setVideoId(vParam);\n\t\telse setErrorMessage(invalidUrlMessage);\n\t}, [videoRawURL, invalidUrlMessage]);\n\n\tuseEffect(() => {\n\t\tif (!modalIsVisible) {\n\t\t\tsetVideoId(\"\");\n\t\t\tsetVideoRawURL(\"\");\n\t\t\tsetErrorMessage(\"\");\n\t\t}\n\t}, [modalIsVisible]);\n\n\treturn (\n\t\t<>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tclassName=\"arkynRichTextInsertVideo\"\n\t\t\t\tonMouseDown={() => setModalIsVisible(true)}\n\t\t\t>\n\t\t\t\t<Video />\n\t\t\t</button>\n\n\t\t\t<ModalContainer\n\t\t\t\tisVisible={modalIsVisible}\n\t\t\t\tmakeInvisible={() => setModalIsVisible(false)}\n\t\t\t>\n\t\t\t\t<ModalHeader>{modalTitle}</ModalHeader>\n\n\t\t\t\t<div className=\"arkynRichTextInsertVideoModalContent\">\n\t\t\t\t\t<Input\n\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\tname=\"richTextVideoURL\"\n\t\t\t\t\t\tlabel={modalInputUrlLabel}\n\t\t\t\t\t\tshowAsterisk\n\t\t\t\t\t\tdefaultValue={videoRawURL}\n\t\t\t\t\t\tonChange={(e) => setVideoRawURL(e.target.value)}\n\t\t\t\t\t\terrorMessage={errorMessage}\n\t\t\t\t\t/>\n\n\t\t\t\t\t{videoId && (\n\t\t\t\t\t\t<iframe\n\t\t\t\t\t\t\twidth=\"auto\"\n\t\t\t\t\t\t\theight=\"315\"\n\t\t\t\t\t\t\tsrc={`https://www.youtube.com/embed/${videoId}`}\n\t\t\t\t\t\t\ttitle=\"YouTube video player\"\n\t\t\t\t\t\t\tallow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\"\n\t\t\t\t\t\t\treferrerPolicy=\"strict-origin-when-cross-origin\"\n\t\t\t\t\t\t\tallowFullScreen\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<ModalFooter>\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tscheme=\"danger\"\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tonClick={() => setModalIsVisible(false)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{modalCancelButton}\n\t\t\t\t\t</Button>\n\n\t\t\t\t\t<Button disabled={!videoId} type=\"button\" onClick={handleMouseDown}>\n\t\t\t\t\t\t{modalConfirmButton}\n\t\t\t\t\t</Button>\n\t\t\t\t</ModalFooter>\n\t\t\t</ModalContainer>\n\t\t</>\n\t);\n}\n\nexport { InsertVideo };\n"],"mappings":";;;;;;;;;;;;AAeA,SAAS,EAAY,GAAiC;CACrD,IAAM,EACL,uBAAoB,YACpB,wBAAqB,aACrB,wBAAqB,iBACrB,gBAAa,iBACb,uBAAoB,mBACjB,GAEE,IAAS,EAAS,GAElB,CAAC,GAAgB,KAAqB,EAAS,EAAK,GACpD,CAAC,GAAS,KAAc,EAAS,EAAE,GACnC,CAAC,GAAa,KAAkB,EAAS,EAAE,GAC3C,CAAC,GAAc,KAAmB,EAAS,EAAE;CAEnD,SAAS,EAAgB,GAAsC;EAG9D,IAFA,EAAM,eAAe,GAEjB,CAAC,KAAW,MAAY,IAAI;EAChC,IAAM,IAAW,iCAAiC;EAQlD,AANA,EAAO,YAAY;GAClB;IAAE,MAAM;IAAa,UAAU,CAAC,EAAE,MAAM,GAAG,CAAC;GAAE;GAC9C;IAAE,MAAM;IAAS,KAAK;IAAU,UAAU,CAAC,EAAE,MAAM,GAAG,CAAC;GAAE;GACzD;IAAE,MAAM;IAAa,UAAU,CAAC,EAAE,MAAM,GAAG,CAAC;GAAE;EAC/C,CAAC,GAED,EAAkB,EAAK;CACxB;CA2BA,OAzBA,QAAgB;EACf,IAAI,CAAC,KAAe,MAAgB,IAAI;EAKxC,IAHA,EAAW,EAAE,GACb,EAAgB,EAAE,GAEd,CAAC,EAAgB,CAAW,GAAG;GAClC,EAAgB,CAAiB;GACjC;EACD;EAGA,IAAM,IAAS,IADU,gBAAgB,EAAY,MAAM,GAAG,CAAC,CAAC,EACjD,CAAA,CAAa,IAAI,GAAG;EACnC,AAAI,IAAQ,EAAW,CAAM,IACxB,EAAgB,CAAiB;CACvC,GAAG,CAAC,GAAa,CAAiB,CAAC,GAEnC,QAAgB;EACf,AAAK,MACJ,EAAW,EAAE,GACb,EAAe,EAAE,GACjB,EAAgB,EAAE;CAEpB,GAAG,CAAC,CAAc,CAAC,GAGlB,kBAAA,GAAA,EAAA,UAAA,CACC,kBAAC,UAAD;EACC,MAAK;EACL,WAAU;EACV,mBAAmB,EAAkB,EAAI;YAEzC,kBAAC,GAAD,CAAQ,CAAA;CACD,CAAA,GAER,kBAAC,GAAD;EACC,WAAW;EACX,qBAAqB,EAAkB,EAAK;YAF7C;GAIC,kBAAC,GAAD,EAAA,UAAc,EAAwB,CAAA;GAEtC,kBAAC,OAAD;IAAK,WAAU;cAAf,CACC,kBAAC,GAAD;KACC,MAAK;KACL,MAAK;KACL,OAAO;KACP,cAAA;KACA,cAAc;KACd,WAAW,MAAM,EAAe,EAAE,OAAO,KAAK;KAChC;IACd,CAAA,GAEA,KACA,kBAAC,UAAD;KACC,OAAM;KACN,QAAO;KACP,KAAK,iCAAiC;KACtC,OAAM;KACN,OAAM;KACN,gBAAe;KACf,iBAAA;IACA,CAAA,CAEE;;GAEL,kBAAC,GAAD,EAAA,UAAA,CACC,kBAAC,GAAD;IACC,MAAK;IACL,QAAO;IACP,SAAQ;IACR,eAAe,EAAkB,EAAK;cAErC;GACM,CAAA,GAER,kBAAC,GAAD;IAAQ,UAAU,CAAC;IAAS,MAAK;IAAS,SAAS;cACjD;GACM,CAAA,CACI,EAAA,CAAA;EACE;GACf,EAAA,CAAA;AAEJ"}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
/* empty css */
|
|
1
2
|
import { jsx as e } from "react/jsx-runtime";
|
|
2
3
|
//#region src/components/richText/leaf/index.tsx
|
|
3
4
|
function t({ attributes: t, children: n, leaf: r }) {
|
|
4
|
-
return r.bold && (n = /* @__PURE__ */ e("strong", { children: n })), r.code && (n = /* @__PURE__ */ e("code", { children: n })), r.italic && (n = /* @__PURE__ */ e("em", { children: n })), r.underline && (n = /* @__PURE__ */ e("u", { children: n })), /* @__PURE__ */ e("
|
|
5
|
+
return r.bold && (n = /* @__PURE__ */ e("strong", { children: n })), r.code && (n = /* @__PURE__ */ e("code", { children: n })), r.italic && (n = /* @__PURE__ */ e("em", { children: n })), r.underline && (n = /* @__PURE__ */ e("u", { children: n })), r.link && (n = /* @__PURE__ */ e("a", {
|
|
6
|
+
className: "arkynLeafLink",
|
|
7
|
+
href: r.href,
|
|
8
|
+
children: n
|
|
9
|
+
})), /* @__PURE__ */ e("span", {
|
|
5
10
|
...t,
|
|
6
11
|
children: n
|
|
7
12
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../../src/components/richText/leaf/index.tsx"],"sourcesContent":["import type { RenderLeafProps } from \"slate-react\";\n\nfunction Leaf({ attributes, children, leaf }: RenderLeafProps) {\n\tif (leaf.bold) children = <strong>{children}</strong>;\n\tif (leaf.code) children = <code>{children}</code>;\n\tif (leaf.italic) children = <em>{children}</em>;\n\tif (leaf.underline) children = <u>{children}</u>;\n\n\treturn <span {...attributes}>{children}</span>;\n}\n\nexport { Leaf };\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../../src/components/richText/leaf/index.tsx"],"sourcesContent":["import type { RenderLeafProps } from \"slate-react\";\nimport \"./styles.css\";\n\nfunction Leaf({ attributes, children, leaf }: RenderLeafProps) {\n\tif (leaf.bold) children = <strong>{children}</strong>;\n\tif (leaf.code) children = <code>{children}</code>;\n\tif (leaf.italic) children = <em>{children}</em>;\n\tif (leaf.underline) children = <u>{children}</u>;\n\tif (leaf.link) {\n\t\tchildren = (\n\t\t\t<a className=\"arkynLeafLink\" href={leaf.href}>\n\t\t\t\t{children}\n\t\t\t</a>\n\t\t);\n\t}\n\n\treturn <span {...attributes}>{children}</span>;\n}\n\nexport { Leaf };\n"],"mappings":";;;AAGA,SAAS,EAAK,EAAE,eAAY,aAAU,WAAyB;CAa9D,OAZI,EAAK,SAAM,IAAW,kBAAC,UAAD,EAAS,YAAiB,CAAA,IAChD,EAAK,SAAM,IAAW,kBAAC,QAAD,EAAO,YAAe,CAAA,IAC5C,EAAK,WAAQ,IAAW,kBAAC,MAAD,EAAK,YAAa,CAAA,IAC1C,EAAK,cAAW,IAAW,kBAAC,KAAD,EAAI,YAAY,CAAA,IAC3C,EAAK,SACR,IACC,kBAAC,KAAD;EAAG,WAAU;EAAgB,MAAM,EAAK;EACtC;CACC,CAAA,IAIE,kBAAC,QAAD;EAAM,GAAI;EAAa;CAAe,CAAA;AAC9C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer arkyn{.arkynLeafLink{color:var(--text-link,#2563eb);text-decoration:underline}}
|
|
@@ -60,8 +60,12 @@
|
|
|
60
60
|
|
|
61
61
|
@layer arkyn{.arkynTabContainer{border-bottom:1px solid var(--border,#e2e8f0);justify-content:start;align-items:center;gap:4px;display:flex;position:relative}}
|
|
62
62
|
|
|
63
|
+
@layer arkyn{.arkynRichTextInsertLink{background-color:var(--background-underground,#fafafa);border:none;border-radius:4px;justify-content:center;align-items:center;width:36px;height:36px;display:flex}.arkynRichTextInsertLink svg{width:20px;height:20px;color:var(--text-heading,#09090b)}.arkynRichTextInsertLink:hover{cursor:pointer;filter:brightness(.98)}.arkynRichTextInsertLinkModalContent{flex-direction:column;gap:16px;width:500px;padding:16px;display:flex}html.dark .arkynRichTextInsertLink:hover{cursor:pointer;filter:brightness(.9)}}
|
|
64
|
+
|
|
63
65
|
@layer arkyn{.arkynRichTextInsertVideo{background-color:var(--background-underground,#fafafa);border:none;border-radius:4px;justify-content:center;align-items:center;width:36px;height:36px;display:flex}.arkynRichTextInsertVideo svg{width:20px;height:20px;color:var(--text-heading,#09090b)}.arkynRichTextInsertVideo:hover{cursor:pointer;filter:brightness(.98)}.arkynRichTextInsertVideoModalContent{flex-direction:column;gap:16px;width:500px;padding:16px;display:flex}.arkynRichTextInsertVideoModalPreviewVideo{border-radius:8px;width:100%}.arkynRichTextInsertVideoModalContent .arkynVideoUpload{min-height:174px}html.dark .arkynRichTextInsertVideo:hover{cursor:pointer;filter:brightness(.9)}}
|
|
64
66
|
|
|
67
|
+
@layer arkyn{.arkynLeafLink{color:var(--text-link,#2563eb);text-decoration:underline}}
|
|
68
|
+
|
|
65
69
|
@layer arkyn{.arkynRichTextMarkButton{background-color:var(--background-underground,#fafafa);border:none;border-radius:4px;justify-content:center;align-items:center;width:36px;height:36px;display:flex}.arkynRichTextMarkButton svg{width:20px;height:20px;color:var(--text-heading,#09090b)}.arkynRichTextMarkButton:hover{cursor:pointer;filter:brightness(.97)}html.dark .arkynRichTextMarkButton:hover{cursor:pointer;filter:brightness(.9)}.arkynRichTextMarkButton.activeTrue{filter:brightness(.97)}html.dark .arkynRichTextMarkButton.activeTrue{filter:brightness(.9)}.arkynRichTextMarkButton.activeTrue svg{color:rgba(var(--spotlight-primary,17, 109, 220), 1)}}
|
|
66
70
|
|
|
67
71
|
@layer arkyn{.arkynRichTextToolbar{background-color:var(--background-underground,#fafafa);border-bottom:1px solid var(--border,#e2e8f0);border-radius:8px 8px 0 0;flex-wrap:wrap;align-items:center;gap:12px 24px;padding:12px 16px;display:flex}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isValidHttpsUrl.js","names":[],"sources":["../../../src/services/isValidHttpsUrl.ts"],"sourcesContent":["function isValidHttpsUrl(url: string): boolean {\n\ttry {\n\t\treturn new URL(url).protocol === \"https:\";\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport { isValidHttpsUrl };\n"],"mappings":";AAAA,SAAS,EAAgB,GAAsB;CAC9C,IAAI;EACH,OAAO,IAAI,IAAI,CAAG,CAAC,CAAC,aAAa;CAClC,QAAQ;EACP,OAAO;CACR;AACD"}
|