@particle-academy/react-fancy 5.29.1 → 5.31.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +1722 -0
  2. package/dist/Card-CuRZivBz.d.cts +136 -0
  3. package/dist/Card-CuRZivBz.d.ts +136 -0
  4. package/dist/card.cjs +12 -6
  5. package/dist/card.d.cts +50 -3
  6. package/dist/card.d.ts +50 -3
  7. package/dist/card.js +3 -1
  8. package/dist/chunk-5F6V5HHT.cjs +399 -0
  9. package/dist/chunk-5F6V5HHT.cjs.map +1 -0
  10. package/dist/{chunk-Y3T7WMT6.cjs → chunk-65HKNZFG.cjs} +2 -2
  11. package/dist/{chunk-Y3T7WMT6.cjs.map → chunk-65HKNZFG.cjs.map} +1 -1
  12. package/dist/chunk-7MSIREUY.cjs +186 -0
  13. package/dist/chunk-7MSIREUY.cjs.map +1 -0
  14. package/dist/{chunk-2HGYDDIQ.js → chunk-ASCATIDJ.js} +3 -3
  15. package/dist/{chunk-2HGYDDIQ.js.map → chunk-ASCATIDJ.js.map} +1 -1
  16. package/dist/{chunk-6GWOB2Z4.js → chunk-CESGP5AF.js} +2 -2
  17. package/dist/{chunk-6GWOB2Z4.js.map → chunk-CESGP5AF.js.map} +1 -1
  18. package/dist/chunk-CF2U25LM.js +392 -0
  19. package/dist/chunk-CF2U25LM.js.map +1 -0
  20. package/dist/chunk-CFH6CJQG.js +3 -0
  21. package/dist/chunk-CFH6CJQG.js.map +1 -0
  22. package/dist/chunk-FJKZ5NOA.cjs +4 -0
  23. package/dist/chunk-FJKZ5NOA.cjs.map +1 -0
  24. package/dist/chunk-NH5XJ6VD.js +184 -0
  25. package/dist/chunk-NH5XJ6VD.js.map +1 -0
  26. package/dist/{chunk-FEUAZW3X.cjs → chunk-SCGE6DCE.cjs} +3 -3
  27. package/dist/{chunk-FEUAZW3X.cjs.map → chunk-SCGE6DCE.cjs.map} +1 -1
  28. package/dist/composer.cjs +2 -2
  29. package/dist/composer.d.cts +34 -1
  30. package/dist/composer.d.ts +34 -1
  31. package/dist/composer.js +1 -1
  32. package/dist/heading.cjs +3 -2
  33. package/dist/heading.js +2 -1
  34. package/dist/index.cjs +28 -27
  35. package/dist/index.d.cts +2 -2
  36. package/dist/index.d.ts +2 -2
  37. package/dist/index.js +6 -5
  38. package/dist/json-editor.cjs +13 -13
  39. package/dist/json-editor.js +2 -2
  40. package/docs/Card.md +170 -20
  41. package/package.json +6 -3
  42. package/dist/Card-BZ5F6-wd.d.cts +0 -50
  43. package/dist/Card-BZ5F6-wd.d.ts +0 -50
  44. package/dist/chunk-5DQ2XUJI.cjs +0 -173
  45. package/dist/chunk-5DQ2XUJI.cjs.map +0 -1
  46. package/dist/chunk-OGEE23SP.cjs +0 -83
  47. package/dist/chunk-OGEE23SP.cjs.map +0 -1
  48. package/dist/chunk-VVL5LMLW.js +0 -81
  49. package/dist/chunk-VVL5LMLW.js.map +0 -1
  50. package/dist/chunk-ZIDBI6JX.js +0 -167
  51. package/dist/chunk-ZIDBI6JX.js.map +0 -1
@@ -0,0 +1,186 @@
1
+ 'use strict';
2
+
3
+ var chunkOUOEQZHY_cjs = require('./chunk-OUOEQZHY.cjs');
4
+ var chunkZU5Q77OT_cjs = require('./chunk-ZU5Q77OT.cjs');
5
+ var react = require('react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ var PREVIEW_CHARS = 20;
9
+ function withSeparators(n) {
10
+ return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
11
+ }
12
+ function endsOf(text) {
13
+ if (text.length <= PREVIEW_CHARS * 2) return text;
14
+ return `${text.slice(0, PREVIEW_CHARS)} \u2026 ${text.slice(-PREVIEW_CHARS)}`;
15
+ }
16
+ var Composer = react.forwardRef(
17
+ function Composer2({
18
+ value: controlledValue,
19
+ defaultValue = "",
20
+ onChange,
21
+ onSubmit,
22
+ placeholder = "Type a message...",
23
+ actions,
24
+ disabled = false,
25
+ className,
26
+ pasteThreshold,
27
+ heldPastes: controlledHeldPastes,
28
+ onHeldPastesChange
29
+ }, ref) {
30
+ const [value, setValue] = chunkOUOEQZHY_cjs.useControllableState(
31
+ controlledValue,
32
+ defaultValue,
33
+ onChange
34
+ );
35
+ const [heldPastes, setHeldPastes] = chunkOUOEQZHY_cjs.useControllableState(
36
+ controlledHeldPastes,
37
+ [],
38
+ onHeldPastesChange
39
+ );
40
+ const textareaRef = react.useRef(null);
41
+ const nextId = react.useRef(0);
42
+ const handlePaste = react.useCallback(
43
+ (e) => {
44
+ if (!pasteThreshold || pasteThreshold <= 0) return;
45
+ const text = e.clipboardData?.getData("text/plain") ?? "";
46
+ if (text.length < pasteThreshold) return;
47
+ e.preventDefault();
48
+ nextId.current += 1;
49
+ const id = `paste-${nextId.current}`;
50
+ setHeldPastes((current) => [...current, { id, text }]);
51
+ },
52
+ [pasteThreshold, setHeldPastes]
53
+ );
54
+ const composeOutgoing = react.useCallback(
55
+ () => [value, ...heldPastes.map((p) => p.text)].filter(Boolean).join("\n\n"),
56
+ [value, heldPastes]
57
+ );
58
+ const hasContent = value.trim().length > 0 || heldPastes.length > 0;
59
+ const handleSubmit = react.useCallback(() => {
60
+ if (!hasContent || disabled) return;
61
+ onSubmit?.(composeOutgoing());
62
+ setValue("");
63
+ setHeldPastes([]);
64
+ textareaRef.current?.focus();
65
+ }, [hasContent, disabled, onSubmit, composeOutgoing, setValue, setHeldPastes]);
66
+ const handleKeyDown = (e) => {
67
+ if (e.key === "Enter" && !e.shiftKey) {
68
+ e.preventDefault();
69
+ handleSubmit();
70
+ }
71
+ };
72
+ const insertPaste = react.useCallback(
73
+ (paste) => {
74
+ setValue((current) => current ? `${current}
75
+
76
+ ${paste.text}` : paste.text);
77
+ setHeldPastes((current) => current.filter((p) => p.id !== paste.id));
78
+ textareaRef.current?.focus();
79
+ },
80
+ [setValue, setHeldPastes]
81
+ );
82
+ const discardPaste = react.useCallback(
83
+ (id) => setHeldPastes((current) => current.filter((p) => p.id !== id)),
84
+ [setHeldPastes]
85
+ );
86
+ return /* @__PURE__ */ jsxRuntime.jsxs(
87
+ "div",
88
+ {
89
+ "data-react-fancy-composer": "",
90
+ ref,
91
+ className: chunkZU5Q77OT_cjs.cn(
92
+ "rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900",
93
+ disabled && "opacity-50",
94
+ className
95
+ ),
96
+ children: [
97
+ heldPastes.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
98
+ "div",
99
+ {
100
+ "data-react-fancy-composer-paste-pills": "",
101
+ className: "flex flex-wrap gap-1.5 px-3 pt-3",
102
+ children: heldPastes.map((paste) => (
103
+ // `group` + `group-hover` rather than hover state: the preview is
104
+ // presentation, and a useState for it would be one more thing an
105
+ // agent could desynchronise.
106
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "group relative inline-flex", children: [
107
+ /* @__PURE__ */ jsxRuntime.jsx(
108
+ "button",
109
+ {
110
+ type: "button",
111
+ "data-react-fancy-composer-paste-pill": paste.id,
112
+ onClick: () => insertPaste(paste),
113
+ disabled,
114
+ "aria-label": `Insert this pasted text, ${withSeparators(paste.text.length)} characters`,
115
+ "aria-describedby": `${paste.id}-preview`,
116
+ className: "inline-flex items-center gap-1.5 rounded-full border border-zinc-200 bg-zinc-50 py-1 pl-2.5 pr-7 text-xs text-zinc-700 transition-colors hover:bg-zinc-100 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-200 dark:hover:bg-zinc-700",
117
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
118
+ "Pasted text \xB7 ",
119
+ withSeparators(paste.text.length),
120
+ " characters"
121
+ ] })
122
+ }
123
+ ),
124
+ /* @__PURE__ */ jsxRuntime.jsx(
125
+ "button",
126
+ {
127
+ type: "button",
128
+ "data-react-fancy-composer-paste-pill-remove": paste.id,
129
+ onClick: () => discardPaste(paste.id),
130
+ disabled,
131
+ "aria-label": "Discard this pasted text",
132
+ className: "absolute right-1 top-1/2 -translate-y-1/2 rounded-full px-1 text-xs text-zinc-400 transition-colors hover:text-zinc-700 dark:hover:text-zinc-100",
133
+ children: "\xD7"
134
+ }
135
+ ),
136
+ /* @__PURE__ */ jsxRuntime.jsx(
137
+ "span",
138
+ {
139
+ id: `${paste.id}-preview`,
140
+ "data-react-fancy-composer-paste-pill-preview": paste.id,
141
+ role: "tooltip",
142
+ className: "pointer-events-none absolute bottom-full left-0 z-10 mb-1 hidden max-w-xs whitespace-pre-wrap break-all rounded-md bg-zinc-900 px-2 py-1 text-xs text-white group-hover:block dark:bg-zinc-100 dark:text-zinc-900",
143
+ children: endsOf(paste.text)
144
+ }
145
+ )
146
+ ] }, paste.id)
147
+ ))
148
+ }
149
+ ),
150
+ /* @__PURE__ */ jsxRuntime.jsx(
151
+ "textarea",
152
+ {
153
+ ref: textareaRef,
154
+ value,
155
+ onChange: (e) => setValue(e.target.value),
156
+ onKeyDown: handleKeyDown,
157
+ onPaste: handlePaste,
158
+ placeholder,
159
+ disabled,
160
+ rows: 3,
161
+ className: "w-full resize-none bg-transparent px-4 pt-3 text-sm outline-none placeholder:text-zinc-400"
162
+ }
163
+ ),
164
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-t border-zinc-100 px-3 py-2 dark:border-zinc-800", children: [
165
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: actions }),
166
+ /* @__PURE__ */ jsxRuntime.jsx(
167
+ "button",
168
+ {
169
+ type: "button",
170
+ onClick: handleSubmit,
171
+ disabled: disabled || !hasContent,
172
+ className: "rounded-lg bg-zinc-900 px-3 py-1.5 text-sm font-medium text-white transition-colors hover:bg-zinc-800 disabled:opacity-50 dark:bg-white dark:text-zinc-900 dark:hover:bg-zinc-100",
173
+ children: "Send"
174
+ }
175
+ )
176
+ ] })
177
+ ]
178
+ }
179
+ );
180
+ }
181
+ );
182
+ Composer.displayName = "Composer";
183
+
184
+ exports.Composer = Composer;
185
+ //# sourceMappingURL=chunk-7MSIREUY.cjs.map
186
+ //# sourceMappingURL=chunk-7MSIREUY.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Composer/Composer.tsx"],"names":["forwardRef","Composer","useControllableState","useRef","useCallback","jsxs","cn","jsx"],"mappings":"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","file":"chunk-7MSIREUY.cjs","sourcesContent":["import { forwardRef, useCallback, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { useControllableState } from \"../../hooks/use-controllable-state\";\nimport type { ComposerProps, HeldPaste } from \"./Composer.types\";\n\n/** Characters of a held paste shown at each end of the hover preview. */\nconst PREVIEW_CHARS = 20;\n\n/**\n * Thousands separators, written out rather than left to `toLocaleString()`.\n *\n * The number exists so someone can size the paste at a glance, and `2,036` sizes\n * faster than `2036`. It is not left to the runtime locale because the label beside\n * it (\"Pasted text\") is not localised either, so a locale-formatted number next to\n * an English noun would be half a translation.\n */\nfunction withSeparators(n: number): string {\n return String(n).replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\n}\n\n/** First and last N characters, middle elided. The ends say WHICH paste this is. */\nfunction endsOf(text: string): string {\n if (text.length <= PREVIEW_CHARS * 2) return text;\n\n return `${text.slice(0, PREVIEW_CHARS)} … ${text.slice(-PREVIEW_CHARS)}`;\n}\n\nexport const Composer = forwardRef<HTMLDivElement, ComposerProps>(\n function Composer(\n {\n value: controlledValue,\n defaultValue = \"\",\n onChange,\n onSubmit,\n placeholder = \"Type a message...\",\n actions,\n disabled = false,\n className,\n pasteThreshold,\n heldPastes: controlledHeldPastes,\n onHeldPastesChange,\n },\n ref,\n ) {\n const [value, setValue] = useControllableState(\n controlledValue,\n defaultValue,\n onChange,\n );\n const [heldPastes, setHeldPastes] = useControllableState<HeldPaste[]>(\n controlledHeldPastes,\n [],\n onHeldPastesChange,\n );\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const nextId = useRef(0);\n\n /*\n * A large paste is held, not inserted.\n *\n * Requested by a consumer who had built and proven it against their own\n * composer: paste a transcript and the input becomes a scrolling wall, so the\n * message you were half way through writing is above the fold and the only way\n * to see what you are about to send is to scroll your own input.\n */\n const handlePaste = useCallback(\n (e: React.ClipboardEvent<HTMLTextAreaElement>) => {\n if (!pasteThreshold || pasteThreshold <= 0) return;\n\n const text = e.clipboardData?.getData(\"text/plain\") ?? \"\";\n\n // Below the threshold, do NOTHING — not even look busy. This is the common\n // case and the easiest thing to regress.\n if (text.length < pasteThreshold) return;\n\n e.preventDefault();\n nextId.current += 1;\n const id = `paste-${nextId.current}`;\n setHeldPastes((current) => [...current, { id, text }]);\n },\n [pasteThreshold, setHeldPastes],\n );\n\n /** Everything the person believes they are sending, in the order they built it. */\n const composeOutgoing = useCallback(\n () => [value, ...heldPastes.map((p) => p.text)].filter(Boolean).join(\"\\n\\n\"),\n [value, heldPastes],\n );\n\n const hasContent = value.trim().length > 0 || heldPastes.length > 0;\n\n const handleSubmit = useCallback(() => {\n if (!hasContent || disabled) return;\n\n /*\n * THE DANGEROUS STATE, and the reason this feature needed a spec rather than\n * a guess. A pill reads as \"your paste is attached\" — so a send that drops it\n * means asking a question about a document without the document, and nobody\n * can tell: the sender believes it went, the receiver sees a question about\n * nothing.\n *\n * Held pastes go out in arrival order, after what was typed, because people\n * type the question BEFORE pasting the thing it is about.\n */\n onSubmit?.(composeOutgoing());\n setValue(\"\");\n setHeldPastes([]);\n textareaRef.current?.focus();\n }, [hasContent, disabled, onSubmit, composeOutgoing, setValue, setHeldPastes]);\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n };\n\n /** Click a pill: dump it into the input, APPENDED to what is already typed. */\n const insertPaste = useCallback(\n (paste: HeldPaste) => {\n setValue((current) => (current ? `${current}\\n\\n${paste.text}` : paste.text));\n setHeldPastes((current) => current.filter((p) => p.id !== paste.id));\n textareaRef.current?.focus();\n },\n [setValue, setHeldPastes],\n );\n\n /** Discard without inserting. Without this the only way out is in-then-delete. */\n const discardPaste = useCallback(\n (id: string) => setHeldPastes((current) => current.filter((p) => p.id !== id)),\n [setHeldPastes],\n );\n\n return (\n <div\n data-react-fancy-composer=\"\"\n ref={ref}\n className={cn(\n \"rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900\",\n disabled && \"opacity-50\",\n className,\n )}\n >\n {heldPastes.length > 0 && (\n <div\n data-react-fancy-composer-paste-pills=\"\"\n className=\"flex flex-wrap gap-1.5 px-3 pt-3\"\n >\n {heldPastes.map((paste) => (\n // `group` + `group-hover` rather than hover state: the preview is\n // presentation, and a useState for it would be one more thing an\n // agent could desynchronise.\n <span key={paste.id} className=\"group relative inline-flex\">\n <button\n type=\"button\"\n data-react-fancy-composer-paste-pill={paste.id}\n onClick={() => insertPaste(paste)}\n disabled={disabled}\n // No `title`. A native tooltip plus the hover preview below\n // renders TWO tooltips on the same hover, at different delays.\n // The label says what clicking does; the preview says what the\n // text is, and it is linked rather than merely adjacent so a\n // screen reader reaches it too.\n aria-label={`Insert this pasted text, ${withSeparators(paste.text.length)} characters`}\n aria-describedby={`${paste.id}-preview`}\n // `pr-7` leaves room for the absolutely-positioned × that sits\n // over this button's right edge. With a smaller right padding the\n // × lands on top of the label.\n className=\"inline-flex items-center gap-1.5 rounded-full border border-zinc-200 bg-zinc-50 py-1 pl-2.5 pr-7 text-xs text-zinc-700 transition-colors hover:bg-zinc-100 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-200 dark:hover:bg-zinc-700\"\n >\n <span>\n Pasted text · {withSeparators(paste.text.length)} characters\n </span>\n </button>\n\n <button\n type=\"button\"\n data-react-fancy-composer-paste-pill-remove={paste.id}\n onClick={() => discardPaste(paste.id)}\n disabled={disabled}\n aria-label=\"Discard this pasted text\"\n className=\"absolute right-1 top-1/2 -translate-y-1/2 rounded-full px-1 text-xs text-zinc-400 transition-colors hover:text-zinc-700 dark:hover:text-zinc-100\"\n >\n ×\n </button>\n\n <span\n id={`${paste.id}-preview`}\n data-react-fancy-composer-paste-pill-preview={paste.id}\n role=\"tooltip\"\n className=\"pointer-events-none absolute bottom-full left-0 z-10 mb-1 hidden max-w-xs whitespace-pre-wrap break-all rounded-md bg-zinc-900 px-2 py-1 text-xs text-white group-hover:block dark:bg-zinc-100 dark:text-zinc-900\"\n >\n {endsOf(paste.text)}\n </span>\n </span>\n ))}\n </div>\n )}\n\n <textarea\n ref={textareaRef}\n value={value}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={handleKeyDown}\n onPaste={handlePaste}\n placeholder={placeholder}\n disabled={disabled}\n rows={3}\n className=\"w-full resize-none bg-transparent px-4 pt-3 text-sm outline-none placeholder:text-zinc-400\"\n />\n <div className=\"flex items-center justify-between border-t border-zinc-100 px-3 py-2 dark:border-zinc-800\">\n <div className=\"flex items-center gap-1\">{actions}</div>\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={disabled || !hasContent}\n className=\"rounded-lg bg-zinc-900 px-3 py-1.5 text-sm font-medium text-white transition-colors hover:bg-zinc-800 disabled:opacity-50 dark:bg-white dark:text-zinc-900 dark:hover:bg-zinc-100\"\n >\n Send\n </button>\n </div>\n </div>\n );\n },\n);\n\nComposer.displayName = \"Composer\";\n"]}
@@ -2,7 +2,6 @@ import { Switch } from './chunk-6ZDXG5VC.js';
2
2
  import { DatePicker } from './chunk-L2FPIFMN.js';
3
3
  import { Input } from './chunk-L5KKMJ2Q.js';
4
4
  import { Select } from './chunk-ZH3SPBVB.js';
5
- import { Text } from './chunk-5RLP77KK.js';
6
5
  import { Textarea } from './chunk-DEFGT75G.js';
7
6
  import { DisplayValue } from './chunk-KKWNKBKR.js';
8
7
  import { resolveOption } from './chunk-GFYSELJF.js';
@@ -10,6 +9,7 @@ import { ColorPicker } from './chunk-6PF5ENWM.js';
10
9
  import { Badge } from './chunk-6FCQD2KQ.js';
11
10
  import { Button } from './chunk-G6VMNSCM.js';
12
11
  import { Callout } from './chunk-ZXZJYVWP.js';
12
+ import { Text } from './chunk-5RLP77KK.js';
13
13
  import { useInlineEdit } from './chunk-OL53XYHF.js';
14
14
  import { cn } from './chunk-MMW2JGKZ.js';
15
15
  import { createContext, forwardRef, useId, useRef, useMemo, useState, useEffect, useCallback, useContext } from 'react';
@@ -1337,5 +1337,5 @@ function buildItems({
1337
1337
  }
1338
1338
 
1339
1339
  export { JSON_FIELD_TYPES, JsonEditor, applyJsonEdit, describeEdit, findJsonConflicts, getAtPath, inferType, parseKeyMap, parsePath, pathToString, resolveKeyRule, typeMatches };
1340
- //# sourceMappingURL=chunk-2HGYDDIQ.js.map
1341
- //# sourceMappingURL=chunk-2HGYDDIQ.js.map
1340
+ //# sourceMappingURL=chunk-ASCATIDJ.js.map
1341
+ //# sourceMappingURL=chunk-ASCATIDJ.js.map