@arkyn/components 3.0.5 → 3.0.7

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.
@@ -1,139 +1,141 @@
1
1
  import { useForm as e } from "../../hooks/useForm.js";
2
2
  import { FieldTemplate as t } from "../../services/fieldTemplate.js";
3
- import { extractTextFromNode as n } from "../../services/extractTextFromNode.js";
4
- import { toggleMark as r } from "../../services/toggleMark.js";
5
- import { hotKeys as i, initialValue as a } from "../../templates/richTextTemplates.js";
6
- import { BlockButton as o } from "./blockButton/index.js";
7
- import { Element as s } from "./element/index.js";
8
- import { InsertImage as c } from "./insertImage/index.js";
3
+ import { clearLinkMarkAtBoundary as n } from "../../services/clearLinkMarkAtBoundary.js";
4
+ import { extractTextFromNode as r } from "../../services/extractTextFromNode.js";
5
+ import { toggleMark as i } from "../../services/toggleMark.js";
6
+ import { hotKeys as a, initialValue as o } from "../../templates/richTextTemplates.js";
7
+ import { BlockButton as s } from "./blockButton/index.js";
8
+ import { Element as c } from "./element/index.js";
9
+ import { InsertImage as l } from "./insertImage/index.js";
9
10
  import { InsertLink as ee } from "./insertLink/index.js";
10
11
  import { InsertVideo as te } from "./insertVideo/index.js";
11
12
  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
+ import { MarkButton as u } from "./markButton/index.js";
14
+ import { Toolbar as d } from "./toolbar/index.js";
14
15
  /* empty css */
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";
18
- import C from "is-hotkey";
19
- import { createEditor as w } from "slate";
20
- import { withHistory as T } from "slate-history";
21
- import { Editable as E, Slate as D, withReact as O } from "slate-react";
16
+ import { useCallback as f, useId as p, useMemo as m, useRef as re, useState as h } from "react";
17
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
18
+ import { AlignCenter as ie, AlignJustify as ae, AlignLeft as oe, AlignRight as se, Bold as ce, Code as v, Heading1 as y, Heading2 as b, Italic as x, Quote as S, Underline as C } from "lucide-react";
19
+ import { isHotkey as w } from "is-hotkey";
20
+ import { createEditor as T } from "slate";
21
+ import { withHistory as E } from "slate-history";
22
+ import { Editable as D, Slate as O, withReact as k } from "slate-react";
22
23
  //#region src/components/richText/index.tsx
23
- function k(k) {
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
+ function A(A) {
25
+ let { name: j, hiddenButtons: M, imageConfig: N, videoConfig: P, linkConfig: F, className: I = "", defaultValue: L = "[]", enforceCharacterLimit: R = !1, onChangeCharactersCount: z, baseErrorMessage: B, maxLimit: V = 1e4, onChange: le, isError: H, label: U, showAsterisk: W, id: ue, orientation: de = "vertical", unShowFieldTemplate: fe = !1 } = A, G = m(() => E(k(T())), []), { fieldErrors: pe } = e(), K = m(() => {
25
26
  try {
26
- let e = JSON.parse(I);
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
+ let e = JSON.parse(L);
28
+ return !Array.isArray(e) || e.length === 0 ? structuredClone(o) : e.every((e) => typeof e == "object" && !!e && "type" in e && "children" in e) ? e : structuredClone(o);
28
29
  } catch {
29
- return structuredClone(a);
30
+ return structuredClone(o);
30
31
  }
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, []);
32
+ }, [L]), [q, me] = h(r(K).length), [he, ge] = h(JSON.stringify(K) || "[]"), [_e, J] = h(!1), Y = re(null), ve = p(), ye = ue || ve, X = B || pe?.[j], be = H || !!X, xe = f(ne, []), Se = f(c, []);
32
33
  function Z(e) {
33
- let t = n(e);
34
- pe(t.length), R?.(t.length), !(L && t.length >= B) && (he(JSON.stringify(e)), le?.(e));
34
+ let t = r(e);
35
+ me(t.length), z?.(t.length), !(R && t.length >= V) && (ge(JSON.stringify(e)), le?.(e));
35
36
  }
36
- let Se = `arkynRichText ${ye || B < q ? "errorTrue" : "errorFalse"} ${ge ? "focusTrue" : "focusFalse"}`, Q = B - q;
37
+ let Ce = `arkynRichText ${be || V < q ? "errorTrue" : "errorFalse"} ${_e ? "focusTrue" : "focusFalse"}`, Q = V - q;
37
38
  function $(e) {
38
- return !j?.includes(e);
39
+ return !M?.includes(e);
39
40
  }
40
- return /* @__PURE__ */ h(t, {
41
- name: A,
42
- label: H,
43
- showAsterisk: U,
44
- className: F,
41
+ return /* @__PURE__ */ g(t, {
42
+ name: j,
43
+ label: U,
44
+ showAsterisk: W,
45
+ className: I,
45
46
  errorMessage: X,
46
- unShowFieldTemplate: de,
47
- orientation: ue,
48
- children: /* @__PURE__ */ g(D, {
47
+ unShowFieldTemplate: fe,
48
+ orientation: de,
49
+ children: /* @__PURE__ */ _(O, {
49
50
  editor: G,
50
51
  initialValue: K,
51
52
  onChange: Z,
52
53
  onValueChange: Z,
53
54
  children: [
54
- /* @__PURE__ */ g("div", {
55
- className: Se,
55
+ /* @__PURE__ */ _("div", {
56
+ className: Ce,
56
57
  children: [
57
- /* @__PURE__ */ g(u, { children: [
58
- $("headingOne") && /* @__PURE__ */ h(o, {
58
+ /* @__PURE__ */ _(d, { children: [
59
+ $("headingOne") && /* @__PURE__ */ g(s, {
59
60
  format: "headingOne",
60
- icon: v
61
+ icon: y
61
62
  }),
62
- $("headingTwo") && /* @__PURE__ */ h(o, {
63
+ $("headingTwo") && /* @__PURE__ */ g(s, {
63
64
  format: "headingTwo",
64
- icon: y
65
+ icon: b
65
66
  }),
66
- $("blockQuote") && /* @__PURE__ */ h(o, {
67
+ $("blockQuote") && /* @__PURE__ */ g(s, {
67
68
  format: "blockQuote",
68
- icon: x
69
+ icon: S
69
70
  }),
70
- $("bold") && /* @__PURE__ */ h(l, {
71
+ $("bold") && /* @__PURE__ */ g(u, {
71
72
  format: "bold",
72
73
  icon: ce
73
74
  }),
74
- $("italic") && /* @__PURE__ */ h(l, {
75
+ $("italic") && /* @__PURE__ */ g(u, {
75
76
  format: "italic",
76
- icon: b
77
+ icon: x
77
78
  }),
78
- $("underline") && /* @__PURE__ */ h(l, {
79
+ $("underline") && /* @__PURE__ */ g(u, {
79
80
  format: "underline",
80
- icon: S
81
+ icon: C
81
82
  }),
82
- $("code") && /* @__PURE__ */ h(l, {
83
+ $("code") && /* @__PURE__ */ g(u, {
83
84
  format: "code",
84
- icon: _
85
+ icon: v
85
86
  }),
86
- $("left") && /* @__PURE__ */ h(o, {
87
+ $("left") && /* @__PURE__ */ g(s, {
87
88
  format: "left",
88
89
  icon: oe
89
90
  }),
90
- $("right") && /* @__PURE__ */ h(o, {
91
+ $("right") && /* @__PURE__ */ g(s, {
91
92
  format: "right",
92
93
  icon: se
93
94
  }),
94
- $("center") && /* @__PURE__ */ h(o, {
95
+ $("center") && /* @__PURE__ */ g(s, {
95
96
  format: "center",
96
97
  icon: ie
97
98
  }),
98
- $("justify") && /* @__PURE__ */ h(o, {
99
+ $("justify") && /* @__PURE__ */ g(s, {
99
100
  format: "justify",
100
101
  icon: ae
101
102
  }),
102
- M && $("image") && /* @__PURE__ */ h(c, { ...M }),
103
- $("video") && /* @__PURE__ */ h(te, { ...N }),
104
- $("link") && /* @__PURE__ */ h(ee, { ...P })
103
+ N && $("image") && /* @__PURE__ */ g(l, { ...N }),
104
+ $("video") && /* @__PURE__ */ g(te, { ...P }),
105
+ $("link") && /* @__PURE__ */ g(ee, { ...F })
105
106
  ] }),
106
- /* @__PURE__ */ h(E, {
107
+ /* @__PURE__ */ g(D, {
107
108
  className: "editorContainer",
108
- renderElement: xe,
109
- renderLeaf: be,
109
+ renderElement: Se,
110
+ renderLeaf: xe,
110
111
  spellCheck: !0,
111
- ref: _e,
112
- id: ve,
112
+ ref: Y,
113
+ id: ye,
113
114
  onFocus: () => J(!0),
114
115
  onBlur: () => J(!1),
115
116
  onKeyDown: (e) => {
116
- for (let t in i) if (C(t, e)) {
117
+ (e.key === " " || e.key === "Enter") && n(G);
118
+ for (let t in a) if (w(t, e)) {
117
119
  e.preventDefault();
118
- let n = i[t];
119
- r(G, n);
120
+ let n = a[t];
121
+ i(G, n);
120
122
  }
121
123
  }
122
124
  }),
123
- Q < 0 && /* @__PURE__ */ h("div", {
125
+ Q < 0 && /* @__PURE__ */ g("div", {
124
126
  className: "restatesCharacters",
125
127
  children: Q
126
128
  })
127
129
  ]
128
130
  }),
129
- /* @__PURE__ */ h("input", {
131
+ /* @__PURE__ */ g("input", {
130
132
  type: "hidden",
131
- name: A,
132
- value: me.slice(0, B)
133
+ name: j,
134
+ value: he.slice(0, V)
133
135
  }),
134
- /* @__PURE__ */ h("input", {
136
+ /* @__PURE__ */ g("input", {
135
137
  type: "hidden",
136
- name: `${A}Count`,
138
+ name: `${j}Count`,
137
139
  value: q
138
140
  })
139
141
  ]
@@ -141,6 +143,6 @@ function k(k) {
141
143
  });
142
144
  }
143
145
  //#endregion
144
- export { k as RichText };
146
+ export { A as RichText };
145
147
 
146
148
  //# sourceMappingURL=index.js.map
@@ -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 { 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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"}
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 { clearLinkMarkAtBoundary } from \"../../services/clearLinkMarkAtBoundary\";\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 * Pressing Space or Enter right after a link automatically stops new text from continuing to be part of that link.\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\tif (event.key === \" \" || event.key === \"Enter\") {\n\t\t\t\t\t\t\t\tclearLinkMarkAtBoundary(editor);\n\t\t\t\t\t\t\t}\n\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,28 @@
1
+ //#region src/hooks/useCopyToClipboard.ts
2
+ function e(e) {
3
+ let t = document.createElement("textarea");
4
+ t.value = e, t.style.position = "fixed", t.style.opacity = "0", document.body.appendChild(t), t.select();
5
+ try {
6
+ return document.execCommand("copy");
7
+ } finally {
8
+ document.body.removeChild(t);
9
+ }
10
+ }
11
+ function t() {
12
+ async function t(t) {
13
+ try {
14
+ return await navigator.clipboard.writeText(t), !0;
15
+ } catch {
16
+ try {
17
+ return e(t);
18
+ } catch {
19
+ return !1;
20
+ }
21
+ }
22
+ }
23
+ return { copyToClipboard: t };
24
+ }
25
+ //#endregion
26
+ export { t as useCopyToClipboard };
27
+
28
+ //# sourceMappingURL=useCopyToClipboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCopyToClipboard.js","names":[],"sources":["../../../src/hooks/useCopyToClipboard.ts"],"sourcesContent":["/**\n * copyWithFallback, copies `text` using a hidden `<textarea>` + `document.execCommand(\"copy\")`.\n *\n * Used internally by `useCopyToClipboard` when `navigator.clipboard` is unavailable or rejects,\n * e.g. insecure contexts (non-HTTPS) or older browsers.\n *\n * @param text - Text to copy.\n * @returns `true` if the copy command succeeded, `false` otherwise.\n */\n\nfunction copyWithFallback(text: string) {\n\tconst textarea = document.createElement(\"textarea\");\n\ttextarea.value = text;\n\ttextarea.style.position = \"fixed\";\n\ttextarea.style.opacity = \"0\";\n\tdocument.body.appendChild(textarea);\n\ttextarea.select();\n\n\ttry {\n\t\treturn document.execCommand(\"copy\");\n\t} finally {\n\t\tdocument.body.removeChild(textarea);\n\t}\n}\n\n/**\n * useCopyToClipboard, copies text to the clipboard and reports whether it succeeded.\n *\n * Tries `navigator.clipboard.writeText` first. If it's unavailable or rejects, falls back\n * to a hidden `<textarea>` with `document.execCommand(\"copy\")`. Never throws; any failure\n * simply resolves to `false`.\n *\n * @returns An object with `copyToClipboard`, an async function that copies `text` and resolves\n * to `true` on success, `false` on failure.\n *\n * @example\n * ```tsx\n * function CopyButton({ value }) {\n * const { copyToClipboard } = useCopyToClipboard();\n *\n * async function handleClick() {\n * const success = await copyToClipboard(value);\n * if (success) console.log(\"Copied!\");\n * }\n *\n * return <button onClick={handleClick}>Copy</button>;\n * }\n * ```\n */\n\nfunction useCopyToClipboard() {\n\tasync function copyToClipboard(text: string) {\n\t\ttry {\n\t\t\tawait navigator.clipboard.writeText(text);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\treturn copyWithFallback(text);\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { copyToClipboard };\n}\n\nexport { useCopyToClipboard };\n"],"mappings":";AAUA,SAAS,EAAiB,GAAc;CACvC,IAAM,IAAW,SAAS,cAAc,UAAU;CAKlD,AAJA,EAAS,QAAQ,GACjB,EAAS,MAAM,WAAW,SAC1B,EAAS,MAAM,UAAU,KACzB,SAAS,KAAK,YAAY,CAAQ,GAClC,EAAS,OAAO;CAEhB,IAAI;EACH,OAAO,SAAS,YAAY,MAAM;CACnC,UAAU;EACT,SAAS,KAAK,YAAY,CAAQ;CACnC;AACD;AA2BA,SAAS,IAAqB;CAC7B,eAAe,EAAgB,GAAc;EAC5C,IAAI;GAEH,OADA,MAAM,UAAU,UAAU,UAAU,CAAI,GACjC;EACR,QAAQ;GACP,IAAI;IACH,OAAO,EAAiB,CAAI;GAC7B,QAAQ;IACP,OAAO;GACR;EACD;CACD;CAEA,OAAO,EAAE,mBAAgB;AAC1B"}
@@ -0,0 +1,12 @@
1
+ import { Editor as e, Range as t } from "slate";
2
+ //#region src/services/clearLinkMarkAtBoundary.ts
3
+ function n(n) {
4
+ let { selection: r } = n;
5
+ if (!r || !t.isCollapsed(r)) return;
6
+ let [i, a] = e.leaf(n, r.anchor);
7
+ i.link && e.isEnd(n, r.anchor, a) && (e.removeMark(n, "link"), e.removeMark(n, "href"));
8
+ }
9
+ //#endregion
10
+ export { n as clearLinkMarkAtBoundary };
11
+
12
+ //# sourceMappingURL=clearLinkMarkAtBoundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clearLinkMarkAtBoundary.js","names":[],"sources":["../../../src/services/clearLinkMarkAtBoundary.ts"],"sourcesContent":["import { Editor, Range } from \"slate\";\n\nfunction clearLinkMarkAtBoundary(editor: Editor) {\n\tconst { selection } = editor;\n\tif (!selection || !Range.isCollapsed(selection)) return;\n\n\tconst [node, path] = Editor.leaf(editor, selection.anchor);\n\tif (!node.link) return;\n\tif (!Editor.isEnd(editor, selection.anchor, path)) return;\n\n\tEditor.removeMark(editor, \"link\");\n\tEditor.removeMark(editor, \"href\");\n}\n\nexport { clearLinkMarkAtBoundary };\n"],"mappings":";;AAEA,SAAS,EAAwB,GAAgB;CAChD,IAAM,EAAE,iBAAc;CACtB,IAAI,CAAC,KAAa,CAAC,EAAM,YAAY,CAAS,GAAG;CAEjD,IAAM,CAAC,GAAM,KAAQ,EAAO,KAAK,GAAQ,EAAU,MAAM;CACpD,EAAK,QACL,EAAO,MAAM,GAAQ,EAAU,QAAQ,CAAI,MAEhD,EAAO,WAAW,GAAQ,MAAM,GAChC,EAAO,WAAW,GAAQ,MAAM;AACjC"}
@@ -0,0 +1,4 @@
1
+ import { Editor } from "slate";
2
+ declare function clearLinkMarkAtBoundary(editor: Editor): void;
3
+ export { clearLinkMarkAtBoundary };
4
+ //# sourceMappingURL=clearLinkMarkAtBoundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clearLinkMarkAtBoundary.d.ts","sourceRoot":"","sources":["../../src/services/clearLinkMarkAtBoundary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAS,MAAM,OAAO,CAAC;AAEtC,iBAAS,uBAAuB,CAAC,MAAM,EAAE,MAAM,QAU9C;AAED,OAAO,EAAE,uBAAuB,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkyn/components",
3
- "version": "3.0.5",
3
+ "version": "3.0.7",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -112,7 +112,7 @@
112
112
  "devDependencies": {
113
113
  "@react-google-maps/api": "^2.20.8",
114
114
  "@react-input/mask": "^2.0.4",
115
- "@testing-library/jest-dom": "^6.9.1",
115
+ "@testing-library/jest-dom": "^7.0.0",
116
116
  "@testing-library/react": "^16.3.2",
117
117
  "@testing-library/user-event": "^14.6.1",
118
118
  "@types/bun": "catalog:",
@@ -715,6 +715,11 @@
715
715
  "types": "./dist/hooks/useAutomation.d.ts",
716
716
  "default": "./dist/modules/hooks/useAutomation.js"
717
717
  },
718
+ "./useCopyToClipboard": {
719
+ "import": "./dist/modules/hooks/useCopyToClipboard.js",
720
+ "types": "./dist/hooks/useCopyToClipboard.d.ts",
721
+ "default": "./dist/modules/hooks/useCopyToClipboard.js"
722
+ },
718
723
  "./useDrawer": {
719
724
  "import": "./dist/modules/hooks/useDrawer.js",
720
725
  "types": "./dist/hooks/useDrawer.d.ts",
@@ -1105,6 +1110,9 @@
1105
1110
  "useAutomation": [
1106
1111
  "./dist/hooks/useAutomation.d.ts"
1107
1112
  ],
1113
+ "useCopyToClipboard": [
1114
+ "./dist/hooks/useCopyToClipboard.d.ts"
1115
+ ],
1108
1116
  "useDrawer": [
1109
1117
  "./dist/hooks/useDrawer.d.ts"
1110
1118
  ],