@code0-tech/pictor 0.10.9 → 0.11.0

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 +1 @@
1
- .editor-input{width:100%;flex:1 1 auto;overflow:hidden;padding:.59rem 0;position:relative;align-self:stretch}.editor-input .cm-editor{height:100%;outline:none!important}.editor-input .cm-content{padding:0;position:relative}.editor-input .cm-line{align-self:stretch;padding-left:0}.editor-input .cm-cursor{border-width:2px;border-radius:50rem;border-color:#fff}
1
+ .input-wrapper__control--single-line{white-space:pre!important;overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.input-wrapper__control--single-line::-webkit-scrollbar{display:none}.editor-input__menu-anchor{position:absolute;left:0;top:0;width:0;height:0;opacity:0;pointer-events:none}.editor-input{width:100%;overflow:hidden;position:relative;align-self:stretch;font-family:Inter,sans-serif;font-size:.8rem;letter-spacing:-.5px;font-weight:400;color:#ffffffbf}.editor-input [data-slate-editor]{flex:1 1 auto;padding:.6rem 0;align-self:stretch;outline:none;caret-color:#fff;min-height:1.2em;white-space:pre-wrap;word-break:break-word}.editor-input [data-slate-node=element]{margin:0;padding:0}.editor-input ::selection{background:#70b3ff40}.editor-input [data-slate-placeholder]{color:#ffffff4d;pointer-events:none;-webkit-user-select:none;user-select:none}
@@ -1,16 +1,27 @@
1
1
  import { default as React } from 'react';
2
2
  import { ValidationProps } from './useForm';
3
3
  import { InputWrapperProps } from './InputWrapper';
4
- import { CompletionContext, CompletionResult } from '@codemirror/autocomplete';
5
- import { Extension } from '@uiw/react-codemirror';
6
- import { StreamLanguage, TagStyle } from '@codemirror/language';
7
- export interface EditorInputProps extends Omit<InputWrapperProps, 'onChange'>, ValidationProps<any> {
8
- language?: StreamLanguage<unknown>;
9
- suggestions?: (context: CompletionContext) => CompletionResult;
10
- extensions?: Extension[];
4
+ import { InputSuggestion } from './InputSuggestion';
5
+ export interface EditorTokenRule {
6
+ pattern: RegExp;
7
+ /**
8
+ * Wrap a matched token. `children` is Slate's leaf content — include it
9
+ * somewhere so cursor positioning stays accurate.
10
+ */
11
+ wrap?: (matchedText: string, children: React.ReactNode, match: RegExpExecArray) => React.ReactNode;
12
+ style?: React.CSSProperties;
13
+ className?: string;
14
+ }
15
+ export interface EditorInputProps extends Omit<InputWrapperProps, "onChange">, ValidationProps<any> {
16
+ tokenRules?: EditorTokenRule[];
17
+ suggestions?: InputSuggestion[];
18
+ suggestionsEmptyState?: React.ReactNode;
19
+ onSuggestionSelect?: (suggestion: InputSuggestion) => void;
11
20
  disabled?: boolean;
12
21
  readonly?: boolean;
13
- tokenStyles?: TagStyle[];
22
+ /** Render as a single-line input: Enter is blocked, newlines in pasted or external values become spaces. */
23
+ singleLine?: boolean;
14
24
  onChange?: (value: string) => void;
25
+ placeholder?: string;
15
26
  }
16
27
  export declare const EditorInput: React.FC<EditorInputProps>;
@@ -1,78 +1,170 @@
1
- import { jsx as c } from "react/jsx-runtime";
2
- import e from "react";
3
- import { InputWrapper as N } from "./InputWrapper.js";
4
- import x from "@uiw/react-codemirror";
5
- import { createTheme as p } from "@uiw/codemirror-themes";
6
- import { tags as t } from "../../node_modules/@lezer/highlight/dist/index.js";
1
+ import { jsx as i, jsxs as P } from "react/jsx-runtime";
2
+ import R, { useMemo as O, useRef as w, useCallback as f, useState as le } from "react";
3
+ import { createEditor as se, Editor as k, Transforms as D, Text as ae, Node as ce } from "slate";
4
+ import { withReact as ue, ReactEditor as pe, Editable as fe, Slate as de } from "slate-react";
5
+ import { withHistory as ge } from "slate-history";
6
+ import { InputWrapper as he } from "./InputWrapper.js";
7
7
  import "../../utils/contextStore.js";
8
- import { mergeComponentProps as B } from "../../utils/component.js";
9
- import { hashToColor as r } from "../../utils/color.js";
8
+ import { mergeComponentProps as me } from "../../utils/component.js";
9
+ import "js-md5";
10
+ import { Menu as xe, MenuTrigger as ye, MenuPortal as we } from "../menu/Menu.js";
11
+ import { InputSuggestionMenuContent as Te, InputSuggestionMenuContentItems as ve } from "./InputSuggestion.js";
10
12
  import '../../assets/components/form/EditorInput.style.css';/* empty css */
11
- const A = [], L = [], P = {
12
- lineNumbers: !1,
13
- foldGutter: !1,
14
- highlightActiveLine: !1,
15
- highlightActiveLineGutter: !1
16
- }, u = {
17
- background: "transparent",
18
- backgroundImage: "",
19
- foreground: "rgba(255,255,255, 0.75)",
20
- caret: "gray",
21
- selection: "rgba(112,179,255,0.25)",
22
- selectionMatch: "rgba(112,179,255,0.1)",
23
- fontSize: "0.8rem",
24
- fontFamily: '"Inter", sans-serif',
25
- letterSpacing: "-0.5px",
26
- fontWeight: "400",
27
- gutterBackground: "transparent",
28
- gutterForeground: "rgba(255,255,255, 0.5)",
29
- gutterBorder: "transparent",
30
- gutterActiveForeground: "rgba(255,255,255, 1)",
31
- lineHighlight: "rgba(255,255,255, 0.1)"
32
- }, h = [{
33
- tag: t.squareBracket,
34
- color: r("squareBracket")
35
- }, {
36
- tag: t.bracket,
37
- color: r("bracket")
38
- }, {
39
- tag: t.string,
40
- color: r("Text")
41
- }, {
42
- tag: t.bool,
43
- color: r("Boolean")
44
- }, {
45
- tag: t.number,
46
- color: r("Number")
47
- }], F = p({
48
- theme: "light",
49
- settings: u,
50
- styles: h
51
- }), Y = e.memo((f) => {
13
+ const B = (g) => (g || "").split(`
14
+ `).map((h) => ({
15
+ type: "paragraph",
16
+ children: [{
17
+ text: h
18
+ }]
19
+ })), F = (g) => g.map((h) => ce.string(h)).join(`
20
+ `), Oe = R.memo((g) => {
52
21
  const {
53
- title: d,
54
- right: T,
55
- left: b,
56
- rightType: E,
57
- leftType: S,
58
- language: o,
59
- description: _,
60
- extensions: n = A,
61
- tokenStyles: a = L,
62
- formValidation: i,
63
- onChange: g,
64
- wrapperComponent: k,
65
- ...l
66
- } = f, y = e.useMemo(() => [...o ? [...n, o] : n], [n, o]), C = e.useMemo(() => a.length === 0 ? F : p({
67
- theme: "light",
68
- settings: u,
69
- styles: [...h, ...a]
70
- }), [a]), m = i?.setValue, M = e.useCallback((s) => {
71
- m?.(s), g?.(s);
72
- }, [m, g]), I = e.useMemo(() => B("editor-input", l), [l]);
73
- return /* @__PURE__ */ c(N, { title: d, description: _, right: T, left: b, rightType: E, leftType: S, formValidation: i, wrapperComponent: k, children: /* @__PURE__ */ c(x, { extensions: y, onChange: M, theme: C, indentWithTab: !1, ...I, basicSetup: P }) });
22
+ title: h,
23
+ right: j,
24
+ left: A,
25
+ rightType: H,
26
+ leftType: K,
27
+ description: $,
28
+ wrapperComponent: v,
29
+ tokenRules: m = [],
30
+ suggestions: s,
31
+ suggestionsEmptyState: L,
32
+ onSuggestionSelect: q,
33
+ formValidation: E,
34
+ onChange: I,
35
+ disabled: z = !1,
36
+ readonly: U = !1,
37
+ singleLine: x = !1,
38
+ placeholder: W,
39
+ value: G,
40
+ initialValue: J,
41
+ defaultValue: Q,
42
+ required: Ce,
43
+ ...X
44
+ } = g, S = String(G ?? J ?? Q ?? ""), c = x ? S.replace(/\n/g, " ") : S, r = O(() => ge(ue(se())), []), Y = O(() => B(c), []), T = w(c);
45
+ R.useEffect(() => {
46
+ if (c === T.current || (T.current = c, F(r.children) === c)) return;
47
+ const e = B(c);
48
+ k.withoutNormalizing(r, () => {
49
+ for (D.delete(r, {
50
+ at: {
51
+ anchor: k.start(r, []),
52
+ focus: k.end(r, [])
53
+ }
54
+ }), D.insertNodes(r, e, {
55
+ at: [0]
56
+ }); r.children.length > e.length; ) D.removeNodes(r, {
57
+ at: [r.children.length - 1]
58
+ });
59
+ });
60
+ }, [c, r]);
61
+ const Z = f(([e, t]) => {
62
+ const l = [];
63
+ if (!ae.isText(e) || !m.length) return l;
64
+ const {
65
+ text: n
66
+ } = e;
67
+ return m.forEach((o, oe) => {
68
+ const ie = new RegExp(o.pattern.source, o.pattern.flags.includes("g") ? o.pattern.flags : o.pattern.flags + "g");
69
+ let p;
70
+ for (; (p = ie.exec(n)) !== null && p[0].length; )
71
+ l.push({
72
+ anchor: {
73
+ path: t,
74
+ offset: p.index
75
+ },
76
+ focus: {
77
+ path: t,
78
+ offset: p.index + p[0].length
79
+ },
80
+ tokenRuleIndex: oe,
81
+ tokenText: p[0],
82
+ tokenMatch: p
83
+ });
84
+ }), l;
85
+ }, [m]), ee = f(({
86
+ attributes: e,
87
+ children: t,
88
+ leaf: l
89
+ }) => {
90
+ const n = l;
91
+ if (n.tokenRuleIndex !== void 0) {
92
+ const o = m[n.tokenRuleIndex];
93
+ if (o)
94
+ return o.wrap ? /* @__PURE__ */ i("span", { ...e, children: o.wrap(n.tokenText ?? "", t, n.tokenMatch ?? []) }) : /* @__PURE__ */ i("span", { ...e, children: /* @__PURE__ */ i("span", { style: o.style, className: o.className, children: t }) });
95
+ }
96
+ return /* @__PURE__ */ i("span", { ...e, children: t });
97
+ }, [m]), [a, d] = le(!1), M = w(null), C = w(null), _ = w(null), u = w(void 0), y = f(() => {
98
+ const e = _.current, t = C.current, l = t?.parentElement;
99
+ if (!e || !t || !l) return;
100
+ let n = null;
101
+ try {
102
+ r.selection && (n = pe.toDOMRange(r, r.selection).getBoundingClientRect());
103
+ } catch {
104
+ n = null;
105
+ }
106
+ (!n || !n.width && !n.height && !n.left && !n.top) && (n = t.getBoundingClientRect());
107
+ const o = l.getBoundingClientRect();
108
+ e.style.left = `${n.left - o.left}px`, e.style.top = `${n.top - o.top}px`, e.style.width = "0px", e.style.height = `${n.height || parseFloat(getComputedStyle(t).lineHeight) || 16}px`;
109
+ }, [r]), N = E?.setValue, te = f((e) => {
110
+ a && y();
111
+ const t = F(e);
112
+ t !== T.current && (T.current = t, N?.(t), I?.(t));
113
+ }, [a, y, N, I]), b = f(() => {
114
+ u.current && clearTimeout(u.current), s && (y(), d(!0));
115
+ }, [s, y]);
116
+ R.useEffect(() => {
117
+ a && y();
118
+ }, [a]);
119
+ const ne = f(() => {
120
+ u.current && clearTimeout(u.current), u.current = setTimeout(() => d(!1), 150);
121
+ }, []), re = f((e) => {
122
+ if (x && e.key === "Enter" && e.preventDefault(), !s) return;
123
+ const t = M.current;
124
+ if (e.ctrlKey && e.code === "Space") {
125
+ e.preventDefault(), a ? t?.focusFirstItem() : b();
126
+ return;
127
+ }
128
+ !a || !t || (e.key === "ArrowDown" ? (e.preventDefault(), t.highlightNextItem()) : e.key === "ArrowUp" ? (e.preventDefault(), t.highlightPreviousItem()) : e.key === "Enter" ? t.selectActiveItem() && (e.preventDefault(), d(!1)) : e.key === "Escape" && (e.preventDefault(), d(!1)));
129
+ }, [s, a, b, x]), V = /* @__PURE__ */ i("div", { ...me("editor-input", X), ref: C, children: /* @__PURE__ */ i(
130
+ fe,
131
+ {
132
+ className: x ? "input-wrapper__control input-wrapper__control--single-line" : "input-wrapper__control",
133
+ decorate: Z,
134
+ renderLeaf: ee,
135
+ readOnly: z || U,
136
+ spellCheck: !1,
137
+ placeholder: W,
138
+ onBlur: ne,
139
+ onFocus: () => {
140
+ u.current && clearTimeout(u.current);
141
+ },
142
+ onKeyDown: re,
143
+ onPaste: x ? (e) => {
144
+ e.preventDefault(), r.insertText(e.clipboardData.getData("text/plain").replace(/\r?\n/g, " "));
145
+ } : void 0
146
+ }
147
+ ) });
148
+ return /* @__PURE__ */ i(de, { editor: r, initialValue: Y, onChange: te, children: /* @__PURE__ */ i(he, { title: h, description: $, right: j, left: A, rightType: H, leftType: K, formValidation: E, wrapperComponent: s ? {
149
+ ...v ?? {},
150
+ style: {
151
+ position: "relative",
152
+ ...v?.style
153
+ }
154
+ } : v, children: s ? /* @__PURE__ */ P(xe, { open: a, modal: !1, onOpenChange: (e) => !e && d(!1), children: [
155
+ V,
156
+ /* @__PURE__ */ i(ye, { asChild: !0, children: /* @__PURE__ */ i("button", { ref: _, type: "button", tabIndex: -1, "aria-hidden": !0, onMouseDown: (e) => e.preventDefault(), className: "editor-input__menu-anchor" }) }),
157
+ /* @__PURE__ */ i(we, { children: /* @__PURE__ */ P(Te, { color: "primary", onInteractOutside: (e) => {
158
+ const t = e.target;
159
+ t && C.current?.contains(t) && e.preventDefault();
160
+ }, children: [
161
+ s.length === 0 && L,
162
+ /* @__PURE__ */ i(ve, { ref: M, suggestions: s, onSuggestionSelect: (e) => {
163
+ q?.(e), d(!1);
164
+ } })
165
+ ] }) })
166
+ ] }) : V }) });
74
167
  });
75
- Y.displayName = "EditorInput";
76
168
  export {
77
- Y as EditorInput
169
+ Oe as EditorInput
78
170
  };
@@ -24,4 +24,4 @@ export interface InputSuggestionMenuContentItemsHandle {
24
24
  clearActiveItem: () => void;
25
25
  }
26
26
  export declare const InputSuggestionMenuContent: React.NamedExoticComponent<InputSuggestionMenuContentProps>;
27
- export declare const InputSuggestionMenuContentItems: React.NamedExoticComponent<InputSuggestionMenuContentItemsProps>;
27
+ export declare const InputSuggestionMenuContentItems: React.NamedExoticComponent<InputSuggestionMenuContentItemsProps & React.RefAttributes<InputSuggestionMenuContentItemsHandle>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code0-tech/pictor",
3
- "version": "0.10.9",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "description": "A simple template for a custom React component library",
6
6
  "scripts": {
@@ -18,8 +18,8 @@
18
18
  "author": "CodeZero",
19
19
  "devDependencies": {
20
20
  "@ark-ui/react": "^5.36.2",
21
- "@babel/plugin-proposal-decorators": "^7.29.0",
22
- "@babel/plugin-transform-class-properties": "^7.28.6",
21
+ "@babel/plugin-proposal-decorators": "^7.29.7",
22
+ "@babel/plugin-transform-class-properties": "^7.29.7",
23
23
  "@codemirror/autocomplete": "^6.20.3",
24
24
  "@codemirror/lang-json": "^6.0.2",
25
25
  "@codemirror/language": "^6.12.3",
@@ -61,6 +61,9 @@
61
61
  "@types/react-syntax-highlighter": "^15.5.13",
62
62
  "@uiw/codemirror-themes": "^4.25.10",
63
63
  "@uiw/react-codemirror": "^4.25.10",
64
+ "slate": "^0.124.1",
65
+ "slate-history": "^0.113.1",
66
+ "slate-react": "^0.125.1",
64
67
  "@uiw/react-json-view": "^2.0.0-alpha.43",
65
68
  "@vitejs/plugin-react": "^5.2.0",
66
69
  "@vitest/browser-playwright": "^4.1.2",
@@ -85,7 +88,7 @@
85
88
  "react-zoom-pan-pinch": "^3.7.0",
86
89
  "rimraf": "^6.1.3",
87
90
  "sass": "^1.99.0",
88
- "sass-loader": "^16.0.7",
91
+ "sass-loader": "^16.0.8",
89
92
  "sonner": "^2.0.7",
90
93
  "storybook": "^10.4.2",
91
94
  "style-loader": "^4.0.0",
@@ -142,6 +145,9 @@
142
145
  "react-resizable-panels": "^4.3.1",
143
146
  "react-zoom-pan-pinch": "^3.7.0",
144
147
  "sonner": "^2.0.7",
148
+ "slate": "^0.124.0",
149
+ "slate-history": "^0.113.0",
150
+ "slate-react": "^0.125.0",
145
151
  "zustand": ">=4.3.0 <5.0.0"
146
152
  },
147
153
  "publishConfig": {