@gradeui/ui 3.3.0 → 4.0.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.
- package/components/ui/media-surface.md +1 -1
- package/dist/composer.d.mts +262 -0
- package/dist/composer.d.ts +262 -0
- package/dist/composer.js +3 -0
- package/dist/composer.js.map +1 -0
- package/dist/composer.mjs +3 -0
- package/dist/composer.mjs.map +1 -0
- package/dist/contracts.js +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/contracts.mjs +1 -1
- package/dist/contracts.mjs.map +1 -1
- package/dist/index.d.mts +28 -327
- package/dist/index.d.ts +28 -327
- package/dist/index.js +70 -70
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +70 -70
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types-DUwnWaxR.d.mts +43 -0
- package/dist/types-DUwnWaxR.d.ts +43 -0
- package/package.json +6 -1
- package/styles/globals.css +430 -0
|
@@ -3,7 +3,7 @@ name: MediaSurface
|
|
|
3
3
|
import: "@gradeui/ui"
|
|
4
4
|
props:
|
|
5
5
|
- aspect?: "video" | "square" | "portrait" | "wide" | "auto" — when omitted, derived from `hint` (album/product/food → square, portrait/poster → portrait, landscape → wide, video/audio/embed/generic → video)
|
|
6
|
-
- radius?: "none" | "sm" | "md" | "lg" | "xl" (default "
|
|
6
|
+
- radius?: "none" | "sm" | "md" | "lg" | "xl" (default "none") — driven by `--gds-media-radius` CSS var. Square by default so a slot mounted flush at the top of a Card lets the Card clip it; set `lg`/`xl` for a standalone rounded image
|
|
7
7
|
- border?: boolean (default false)
|
|
8
8
|
- loading?: boolean — renders the muted skeleton overlay
|
|
9
9
|
- hint?: "album" | "portrait" | "landscape" | "poster" | "product" | "food" | "video" | "audio" | "embed" | "3d" | "generic" (default "generic") — picks the placeholder glyph + the default aspect + the future generation provider
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { LexicalEditor } from 'lexical';
|
|
3
|
+
import { a as DemoSpeed, b as DemoTrigger } from './types-DUwnWaxR.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Composer — the generic text composition surface for the design system.
|
|
7
|
+
*
|
|
8
|
+
* The answer wherever a user is composing a message: AI chat input,
|
|
9
|
+
* comment thread reply, post-body editor, future copilot panels.
|
|
10
|
+
* Replaces the textarea-with-buttons pattern that hosts kept rolling
|
|
11
|
+
* by hand.
|
|
12
|
+
*
|
|
13
|
+
* Built on Lexical (Meta's React-first editor framework) so it can do:
|
|
14
|
+
* - rich text formatting (B / I / U / S / code, headings, blockquote,
|
|
15
|
+
* pullquote, lists)
|
|
16
|
+
* - mentions and slash commands via lexical-beautiful-mentions, with
|
|
17
|
+
* a typeahead popover and theme-able tokens
|
|
18
|
+
* - image attachments (paperclip + clipboard paste) when opted in
|
|
19
|
+
* - scripted demo playback for marketing surfaces (types text, opens
|
|
20
|
+
* mention popovers, applies formatting, all via the same step
|
|
21
|
+
* vocabulary as <Code>)
|
|
22
|
+
*
|
|
23
|
+
* Slot-based composition for the action row: hosts that need custom
|
|
24
|
+
* affordances (template picker, voice button, attach-document) pass
|
|
25
|
+
* `leftActions` / `rightActions`. Default Send / Stop / paperclip
|
|
26
|
+
* render only when the host hasn't replaced the slot.
|
|
27
|
+
*
|
|
28
|
+
* Hosts that want the canned "chat composer with paperclip + send"
|
|
29
|
+
* preset should reach for `<AIChatComposer>` instead, which configures
|
|
30
|
+
* this Composer with the right slots wired up.
|
|
31
|
+
*
|
|
32
|
+
* Scripted demos: same `speed` / `trigger` / `play` / `loop` vocabulary
|
|
33
|
+
* as `<Code>`, sharing the underlying `useScriptedDemo` hook from
|
|
34
|
+
* `lib/demo/`. The Composer adds its own verbs (`mention`, `format`,
|
|
35
|
+
* `select`, `submit`) on top of the universal `type` / `wait` / `clear`.
|
|
36
|
+
*/
|
|
37
|
+
type ComposerFormat = "bold" | "italic" | "underline" | "strikethrough" | "code" | "h1" | "h2" | "h3" | "blockquote" | "pullquote" | "ul" | "ol";
|
|
38
|
+
interface ComposerMentionItem {
|
|
39
|
+
id: string;
|
|
40
|
+
/** Display value (without the trigger char). */
|
|
41
|
+
value: string;
|
|
42
|
+
/** Optional secondary label shown in the suggester. */
|
|
43
|
+
label?: string;
|
|
44
|
+
/** Avatar URL or initials for the suggester row. */
|
|
45
|
+
avatar?: string;
|
|
46
|
+
/** Arbitrary payload the host can attach to the mention. */
|
|
47
|
+
data?: Record<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
interface ComposerTriggerConfig {
|
|
50
|
+
/** The trigger character, eg. "@" or "/". */
|
|
51
|
+
char: string;
|
|
52
|
+
/**
|
|
53
|
+
* Items to populate the suggester. Either a static array or a
|
|
54
|
+
* resolver function (sync or async) that receives the typed query.
|
|
55
|
+
* The plugin filters automatically when items is an array.
|
|
56
|
+
*/
|
|
57
|
+
items: ComposerMentionItem[] | ((query: string) => ComposerMentionItem[] | Promise<ComposerMentionItem[]>);
|
|
58
|
+
/**
|
|
59
|
+
* Whether to strip the trigger char on insert. Defaults: keep for
|
|
60
|
+
* "@" (mentions read as "@alice"), strip for "/" (commands read as
|
|
61
|
+
* "Insert image" not "/insert-image").
|
|
62
|
+
*/
|
|
63
|
+
stripTrigger?: boolean;
|
|
64
|
+
}
|
|
65
|
+
interface ComposerAttachmentConfig {
|
|
66
|
+
/** Master enable. Set true on the prop to use defaults, or pass a config object. */
|
|
67
|
+
enabled?: boolean;
|
|
68
|
+
/** HTML accept attribute on the file input. Default "image/*". */
|
|
69
|
+
accept?: string;
|
|
70
|
+
/** Max number of attachments. Default 10. */
|
|
71
|
+
maxItems?: number;
|
|
72
|
+
/** Allow multiple selection in the file picker. Default true. */
|
|
73
|
+
multiple?: boolean;
|
|
74
|
+
}
|
|
75
|
+
interface ComposerAttachment {
|
|
76
|
+
id: string;
|
|
77
|
+
file: File;
|
|
78
|
+
/** Object URL owned by the composer. Hosts must NOT revoke it. */
|
|
79
|
+
previewUrl: string;
|
|
80
|
+
name: string;
|
|
81
|
+
}
|
|
82
|
+
interface ComposerContent {
|
|
83
|
+
/** Plain text representation of the editor contents (whitespace preserved). */
|
|
84
|
+
text: string;
|
|
85
|
+
/** Lexical editor state serialised to JSON (for round-trip persistence). */
|
|
86
|
+
json: string;
|
|
87
|
+
/** Resolved mention tokens in document order. */
|
|
88
|
+
mentions: Array<{
|
|
89
|
+
trigger: string;
|
|
90
|
+
value: string;
|
|
91
|
+
data?: Record<string, unknown>;
|
|
92
|
+
}>;
|
|
93
|
+
}
|
|
94
|
+
interface ComposerHandle {
|
|
95
|
+
/** Run a demo script imperatively (vs. via `steps` + `trigger="manual"`). */
|
|
96
|
+
play: (steps: ComposerStep[]) => void;
|
|
97
|
+
/** Cancel an in-flight demo. Idempotent. */
|
|
98
|
+
stop: () => void;
|
|
99
|
+
/**
|
|
100
|
+
* One-shot replay of the configured `steps`. Cancels any in-flight
|
|
101
|
+
* run, clears the editor, replays from step 0. Pass a delay (ms) to
|
|
102
|
+
* schedule the replay (useful for chaining demos). Requires `steps`
|
|
103
|
+
* to be configured.
|
|
104
|
+
*/
|
|
105
|
+
restart: (delayMs?: number) => void;
|
|
106
|
+
/** Move focus into the editor. */
|
|
107
|
+
focus: () => void;
|
|
108
|
+
/** Wipe the editor. */
|
|
109
|
+
clear: () => void;
|
|
110
|
+
/** Insert plain text at the current selection. */
|
|
111
|
+
insert: (text: string) => void;
|
|
112
|
+
/** Snapshot the current content + mentions. */
|
|
113
|
+
getContent: () => ComposerContent;
|
|
114
|
+
/** Direct access to the underlying Lexical editor (escape hatch). */
|
|
115
|
+
getEditor: () => LexicalEditor | null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Demo step vocabulary for Composer scripts. Shares `type` / `wait` /
|
|
119
|
+
* `clear` with the universal `lib/demo` verbs; adds composer-specific
|
|
120
|
+
* `mention`, `format`, `select`, `newline`, `submit` on top.
|
|
121
|
+
*/
|
|
122
|
+
type ComposerStep = {
|
|
123
|
+
type: "type";
|
|
124
|
+
text: string;
|
|
125
|
+
speed?: DemoSpeed;
|
|
126
|
+
} | {
|
|
127
|
+
type: "wait";
|
|
128
|
+
ms: number;
|
|
129
|
+
} | {
|
|
130
|
+
type: "clear";
|
|
131
|
+
} | {
|
|
132
|
+
type: "newline";
|
|
133
|
+
} | {
|
|
134
|
+
type: "submit";
|
|
135
|
+
} | {
|
|
136
|
+
type: "mention";
|
|
137
|
+
/** Trigger char (must match a registered ComposerTriggerConfig.char). */
|
|
138
|
+
trigger: string;
|
|
139
|
+
/** Value to insert (without the trigger). Looks up the matching item by `value`. */
|
|
140
|
+
value: string;
|
|
141
|
+
/** Optional pre-typed query — the demo types this after the trigger char before "selecting" the value, to show the typeahead in action. */
|
|
142
|
+
query?: string;
|
|
143
|
+
} | {
|
|
144
|
+
type: "format";
|
|
145
|
+
format: ComposerFormat;
|
|
146
|
+
} | {
|
|
147
|
+
type: "select";
|
|
148
|
+
/** Substring to find and select. First match wins. */
|
|
149
|
+
text: string;
|
|
150
|
+
};
|
|
151
|
+
interface ComposerProps {
|
|
152
|
+
/** Placeholder copy shown when empty. */
|
|
153
|
+
placeholder?: string;
|
|
154
|
+
/** Initial plain text content. For richer initial state, use `initialJson`. */
|
|
155
|
+
initialText?: string;
|
|
156
|
+
/** Initial Lexical state JSON (from a previous `onSubmit` round-trip). */
|
|
157
|
+
initialJson?: string;
|
|
158
|
+
/**
|
|
159
|
+
* Available formats. Pass false to disable rich text entirely
|
|
160
|
+
* (plain text mode, half the bundle weight, no toolbar). Default
|
|
161
|
+
* enables a sensible set for most chat / comment surfaces.
|
|
162
|
+
*/
|
|
163
|
+
formats?: ComposerFormat[] | false;
|
|
164
|
+
/**
|
|
165
|
+
* Render the formatting toolbar. Default false because most uses
|
|
166
|
+
* are short-form. Set true (or "top") to show the toolbar above the
|
|
167
|
+
* editor. "floating" is planned; not yet implemented.
|
|
168
|
+
*/
|
|
169
|
+
toolbar?: boolean | "top";
|
|
170
|
+
/**
|
|
171
|
+
* Mention / slash command configs. Each entry registers one trigger
|
|
172
|
+
* char and its items. Pass `[{ char: "@", items: people }, { char: "/", items: commands }]`
|
|
173
|
+
* for the common chat-app setup.
|
|
174
|
+
*/
|
|
175
|
+
triggers?: ComposerTriggerConfig[];
|
|
176
|
+
/**
|
|
177
|
+
* Image attachments (paperclip + clipboard paste). Pass true for
|
|
178
|
+
* defaults, an object to customise, or omit/false to skip the
|
|
179
|
+
* attachment plumbing entirely.
|
|
180
|
+
*/
|
|
181
|
+
attachments?: boolean | ComposerAttachmentConfig;
|
|
182
|
+
/** Fires when the user submits (Enter, click Send, or scripted `submit` step). */
|
|
183
|
+
onSubmit?: (content: ComposerContent, attachments?: ComposerAttachment[]) => void;
|
|
184
|
+
/**
|
|
185
|
+
* Fires on every editor change with the current plain text. Use for
|
|
186
|
+
* length validation, controlled-value bridges (eg. AIChatComposer
|
|
187
|
+
* forwarding to a host's `value`/`onChange` pair), or live preview
|
|
188
|
+
* surfaces. Cheap, called frequently; debounce if you need the
|
|
189
|
+
* Lexical state JSON (use `getContent()` via ref instead).
|
|
190
|
+
*/
|
|
191
|
+
onChange?: (text: string) => void;
|
|
192
|
+
/**
|
|
193
|
+
* Loading state — disables the editor + paperclip and swaps the
|
|
194
|
+
* default Send button for Stop. Has no effect when `rightActions`
|
|
195
|
+
* overrides the default Send.
|
|
196
|
+
*/
|
|
197
|
+
isLoading?: boolean;
|
|
198
|
+
/** Stop handler — required for Stop to be active when loading. */
|
|
199
|
+
onStop?: () => void;
|
|
200
|
+
/** Hard character cap. */
|
|
201
|
+
maxLength?: number;
|
|
202
|
+
/** Auto-focus the editor on mount. Default false. */
|
|
203
|
+
autoFocus?: boolean;
|
|
204
|
+
/** Whether Enter submits. Default true (Shift-Enter still inserts a newline). */
|
|
205
|
+
submitOnEnter?: boolean;
|
|
206
|
+
/**
|
|
207
|
+
* Custom content for the left action slot. Replaces the default
|
|
208
|
+
* paperclip button when `attachments` is enabled.
|
|
209
|
+
*/
|
|
210
|
+
leftActions?: React.ReactNode;
|
|
211
|
+
/**
|
|
212
|
+
* Custom content for the right action slot. Replaces the default
|
|
213
|
+
* Send / Stop button. Use the `useComposer()` hook inside to access
|
|
214
|
+
* imperative methods.
|
|
215
|
+
*/
|
|
216
|
+
rightActions?: React.ReactNode;
|
|
217
|
+
/** Hide the default Send button without replacing it. */
|
|
218
|
+
hideSend?: boolean;
|
|
219
|
+
/** Scripted demo steps. */
|
|
220
|
+
steps?: ComposerStep[];
|
|
221
|
+
/** What kicks the script off. Defaults to "mount". */
|
|
222
|
+
trigger?: DemoTrigger;
|
|
223
|
+
/** For trigger="manual" — flip true to play. */
|
|
224
|
+
play?: boolean;
|
|
225
|
+
/** Animation feel. */
|
|
226
|
+
speed?: DemoSpeed;
|
|
227
|
+
/** Loop the script forever. */
|
|
228
|
+
loop?: boolean;
|
|
229
|
+
/**
|
|
230
|
+
* Pause between loop iterations (ms). Defaults to 2000. Marketing
|
|
231
|
+
* heroes that want the demo to breathe between repeats bump this
|
|
232
|
+
* higher; tight inline demos drop it.
|
|
233
|
+
*/
|
|
234
|
+
loopDelay?: number;
|
|
235
|
+
/**
|
|
236
|
+
* Fires once per loop cycle, AFTER the loopDelay pause and BEFORE
|
|
237
|
+
* the script replays. Use to reset parent state that the script
|
|
238
|
+
* mutated via onSubmit (e.g., wipe a messages list back to its
|
|
239
|
+
* seed before the demo types into it again). The editor is cleared
|
|
240
|
+
* automatically — you only need this hook if state outside the
|
|
241
|
+
* Composer needs to reset too.
|
|
242
|
+
*/
|
|
243
|
+
onLoopReset?: () => void;
|
|
244
|
+
/**
|
|
245
|
+
* Bare mode — strip the card chrome (border / bg / rounding). Use
|
|
246
|
+
* when embedding inside an existing card or column layout.
|
|
247
|
+
*/
|
|
248
|
+
bare?: boolean;
|
|
249
|
+
/**
|
|
250
|
+
* Read-only mode — disables editing AND focusability. Programmatic
|
|
251
|
+
* updates (including scripted demo playback) still work. Use for
|
|
252
|
+
* marketing surfaces that render a Composer purely for show, so the
|
|
253
|
+
* scripted typing doesn't steal focus from other inputs on the page.
|
|
254
|
+
* Hides the default Send / paperclip action row.
|
|
255
|
+
*/
|
|
256
|
+
readOnly?: boolean;
|
|
257
|
+
className?: string;
|
|
258
|
+
}
|
|
259
|
+
declare const Composer: React.ForwardRefExoticComponent<ComposerProps & React.RefAttributes<ComposerHandle>>;
|
|
260
|
+
declare const ComposerReply: React.ForwardRefExoticComponent<ComposerProps & React.RefAttributes<ComposerHandle>>;
|
|
261
|
+
|
|
262
|
+
export { Composer, type ComposerAttachment, type ComposerAttachmentConfig, type ComposerContent, type ComposerFormat, type ComposerHandle, type ComposerMentionItem, type ComposerProps, ComposerReply, type ComposerStep, type ComposerTriggerConfig };
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { LexicalEditor } from 'lexical';
|
|
3
|
+
import { a as DemoSpeed, b as DemoTrigger } from './types-DUwnWaxR.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Composer — the generic text composition surface for the design system.
|
|
7
|
+
*
|
|
8
|
+
* The answer wherever a user is composing a message: AI chat input,
|
|
9
|
+
* comment thread reply, post-body editor, future copilot panels.
|
|
10
|
+
* Replaces the textarea-with-buttons pattern that hosts kept rolling
|
|
11
|
+
* by hand.
|
|
12
|
+
*
|
|
13
|
+
* Built on Lexical (Meta's React-first editor framework) so it can do:
|
|
14
|
+
* - rich text formatting (B / I / U / S / code, headings, blockquote,
|
|
15
|
+
* pullquote, lists)
|
|
16
|
+
* - mentions and slash commands via lexical-beautiful-mentions, with
|
|
17
|
+
* a typeahead popover and theme-able tokens
|
|
18
|
+
* - image attachments (paperclip + clipboard paste) when opted in
|
|
19
|
+
* - scripted demo playback for marketing surfaces (types text, opens
|
|
20
|
+
* mention popovers, applies formatting, all via the same step
|
|
21
|
+
* vocabulary as <Code>)
|
|
22
|
+
*
|
|
23
|
+
* Slot-based composition for the action row: hosts that need custom
|
|
24
|
+
* affordances (template picker, voice button, attach-document) pass
|
|
25
|
+
* `leftActions` / `rightActions`. Default Send / Stop / paperclip
|
|
26
|
+
* render only when the host hasn't replaced the slot.
|
|
27
|
+
*
|
|
28
|
+
* Hosts that want the canned "chat composer with paperclip + send"
|
|
29
|
+
* preset should reach for `<AIChatComposer>` instead, which configures
|
|
30
|
+
* this Composer with the right slots wired up.
|
|
31
|
+
*
|
|
32
|
+
* Scripted demos: same `speed` / `trigger` / `play` / `loop` vocabulary
|
|
33
|
+
* as `<Code>`, sharing the underlying `useScriptedDemo` hook from
|
|
34
|
+
* `lib/demo/`. The Composer adds its own verbs (`mention`, `format`,
|
|
35
|
+
* `select`, `submit`) on top of the universal `type` / `wait` / `clear`.
|
|
36
|
+
*/
|
|
37
|
+
type ComposerFormat = "bold" | "italic" | "underline" | "strikethrough" | "code" | "h1" | "h2" | "h3" | "blockquote" | "pullquote" | "ul" | "ol";
|
|
38
|
+
interface ComposerMentionItem {
|
|
39
|
+
id: string;
|
|
40
|
+
/** Display value (without the trigger char). */
|
|
41
|
+
value: string;
|
|
42
|
+
/** Optional secondary label shown in the suggester. */
|
|
43
|
+
label?: string;
|
|
44
|
+
/** Avatar URL or initials for the suggester row. */
|
|
45
|
+
avatar?: string;
|
|
46
|
+
/** Arbitrary payload the host can attach to the mention. */
|
|
47
|
+
data?: Record<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
interface ComposerTriggerConfig {
|
|
50
|
+
/** The trigger character, eg. "@" or "/". */
|
|
51
|
+
char: string;
|
|
52
|
+
/**
|
|
53
|
+
* Items to populate the suggester. Either a static array or a
|
|
54
|
+
* resolver function (sync or async) that receives the typed query.
|
|
55
|
+
* The plugin filters automatically when items is an array.
|
|
56
|
+
*/
|
|
57
|
+
items: ComposerMentionItem[] | ((query: string) => ComposerMentionItem[] | Promise<ComposerMentionItem[]>);
|
|
58
|
+
/**
|
|
59
|
+
* Whether to strip the trigger char on insert. Defaults: keep for
|
|
60
|
+
* "@" (mentions read as "@alice"), strip for "/" (commands read as
|
|
61
|
+
* "Insert image" not "/insert-image").
|
|
62
|
+
*/
|
|
63
|
+
stripTrigger?: boolean;
|
|
64
|
+
}
|
|
65
|
+
interface ComposerAttachmentConfig {
|
|
66
|
+
/** Master enable. Set true on the prop to use defaults, or pass a config object. */
|
|
67
|
+
enabled?: boolean;
|
|
68
|
+
/** HTML accept attribute on the file input. Default "image/*". */
|
|
69
|
+
accept?: string;
|
|
70
|
+
/** Max number of attachments. Default 10. */
|
|
71
|
+
maxItems?: number;
|
|
72
|
+
/** Allow multiple selection in the file picker. Default true. */
|
|
73
|
+
multiple?: boolean;
|
|
74
|
+
}
|
|
75
|
+
interface ComposerAttachment {
|
|
76
|
+
id: string;
|
|
77
|
+
file: File;
|
|
78
|
+
/** Object URL owned by the composer. Hosts must NOT revoke it. */
|
|
79
|
+
previewUrl: string;
|
|
80
|
+
name: string;
|
|
81
|
+
}
|
|
82
|
+
interface ComposerContent {
|
|
83
|
+
/** Plain text representation of the editor contents (whitespace preserved). */
|
|
84
|
+
text: string;
|
|
85
|
+
/** Lexical editor state serialised to JSON (for round-trip persistence). */
|
|
86
|
+
json: string;
|
|
87
|
+
/** Resolved mention tokens in document order. */
|
|
88
|
+
mentions: Array<{
|
|
89
|
+
trigger: string;
|
|
90
|
+
value: string;
|
|
91
|
+
data?: Record<string, unknown>;
|
|
92
|
+
}>;
|
|
93
|
+
}
|
|
94
|
+
interface ComposerHandle {
|
|
95
|
+
/** Run a demo script imperatively (vs. via `steps` + `trigger="manual"`). */
|
|
96
|
+
play: (steps: ComposerStep[]) => void;
|
|
97
|
+
/** Cancel an in-flight demo. Idempotent. */
|
|
98
|
+
stop: () => void;
|
|
99
|
+
/**
|
|
100
|
+
* One-shot replay of the configured `steps`. Cancels any in-flight
|
|
101
|
+
* run, clears the editor, replays from step 0. Pass a delay (ms) to
|
|
102
|
+
* schedule the replay (useful for chaining demos). Requires `steps`
|
|
103
|
+
* to be configured.
|
|
104
|
+
*/
|
|
105
|
+
restart: (delayMs?: number) => void;
|
|
106
|
+
/** Move focus into the editor. */
|
|
107
|
+
focus: () => void;
|
|
108
|
+
/** Wipe the editor. */
|
|
109
|
+
clear: () => void;
|
|
110
|
+
/** Insert plain text at the current selection. */
|
|
111
|
+
insert: (text: string) => void;
|
|
112
|
+
/** Snapshot the current content + mentions. */
|
|
113
|
+
getContent: () => ComposerContent;
|
|
114
|
+
/** Direct access to the underlying Lexical editor (escape hatch). */
|
|
115
|
+
getEditor: () => LexicalEditor | null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Demo step vocabulary for Composer scripts. Shares `type` / `wait` /
|
|
119
|
+
* `clear` with the universal `lib/demo` verbs; adds composer-specific
|
|
120
|
+
* `mention`, `format`, `select`, `newline`, `submit` on top.
|
|
121
|
+
*/
|
|
122
|
+
type ComposerStep = {
|
|
123
|
+
type: "type";
|
|
124
|
+
text: string;
|
|
125
|
+
speed?: DemoSpeed;
|
|
126
|
+
} | {
|
|
127
|
+
type: "wait";
|
|
128
|
+
ms: number;
|
|
129
|
+
} | {
|
|
130
|
+
type: "clear";
|
|
131
|
+
} | {
|
|
132
|
+
type: "newline";
|
|
133
|
+
} | {
|
|
134
|
+
type: "submit";
|
|
135
|
+
} | {
|
|
136
|
+
type: "mention";
|
|
137
|
+
/** Trigger char (must match a registered ComposerTriggerConfig.char). */
|
|
138
|
+
trigger: string;
|
|
139
|
+
/** Value to insert (without the trigger). Looks up the matching item by `value`. */
|
|
140
|
+
value: string;
|
|
141
|
+
/** Optional pre-typed query — the demo types this after the trigger char before "selecting" the value, to show the typeahead in action. */
|
|
142
|
+
query?: string;
|
|
143
|
+
} | {
|
|
144
|
+
type: "format";
|
|
145
|
+
format: ComposerFormat;
|
|
146
|
+
} | {
|
|
147
|
+
type: "select";
|
|
148
|
+
/** Substring to find and select. First match wins. */
|
|
149
|
+
text: string;
|
|
150
|
+
};
|
|
151
|
+
interface ComposerProps {
|
|
152
|
+
/** Placeholder copy shown when empty. */
|
|
153
|
+
placeholder?: string;
|
|
154
|
+
/** Initial plain text content. For richer initial state, use `initialJson`. */
|
|
155
|
+
initialText?: string;
|
|
156
|
+
/** Initial Lexical state JSON (from a previous `onSubmit` round-trip). */
|
|
157
|
+
initialJson?: string;
|
|
158
|
+
/**
|
|
159
|
+
* Available formats. Pass false to disable rich text entirely
|
|
160
|
+
* (plain text mode, half the bundle weight, no toolbar). Default
|
|
161
|
+
* enables a sensible set for most chat / comment surfaces.
|
|
162
|
+
*/
|
|
163
|
+
formats?: ComposerFormat[] | false;
|
|
164
|
+
/**
|
|
165
|
+
* Render the formatting toolbar. Default false because most uses
|
|
166
|
+
* are short-form. Set true (or "top") to show the toolbar above the
|
|
167
|
+
* editor. "floating" is planned; not yet implemented.
|
|
168
|
+
*/
|
|
169
|
+
toolbar?: boolean | "top";
|
|
170
|
+
/**
|
|
171
|
+
* Mention / slash command configs. Each entry registers one trigger
|
|
172
|
+
* char and its items. Pass `[{ char: "@", items: people }, { char: "/", items: commands }]`
|
|
173
|
+
* for the common chat-app setup.
|
|
174
|
+
*/
|
|
175
|
+
triggers?: ComposerTriggerConfig[];
|
|
176
|
+
/**
|
|
177
|
+
* Image attachments (paperclip + clipboard paste). Pass true for
|
|
178
|
+
* defaults, an object to customise, or omit/false to skip the
|
|
179
|
+
* attachment plumbing entirely.
|
|
180
|
+
*/
|
|
181
|
+
attachments?: boolean | ComposerAttachmentConfig;
|
|
182
|
+
/** Fires when the user submits (Enter, click Send, or scripted `submit` step). */
|
|
183
|
+
onSubmit?: (content: ComposerContent, attachments?: ComposerAttachment[]) => void;
|
|
184
|
+
/**
|
|
185
|
+
* Fires on every editor change with the current plain text. Use for
|
|
186
|
+
* length validation, controlled-value bridges (eg. AIChatComposer
|
|
187
|
+
* forwarding to a host's `value`/`onChange` pair), or live preview
|
|
188
|
+
* surfaces. Cheap, called frequently; debounce if you need the
|
|
189
|
+
* Lexical state JSON (use `getContent()` via ref instead).
|
|
190
|
+
*/
|
|
191
|
+
onChange?: (text: string) => void;
|
|
192
|
+
/**
|
|
193
|
+
* Loading state — disables the editor + paperclip and swaps the
|
|
194
|
+
* default Send button for Stop. Has no effect when `rightActions`
|
|
195
|
+
* overrides the default Send.
|
|
196
|
+
*/
|
|
197
|
+
isLoading?: boolean;
|
|
198
|
+
/** Stop handler — required for Stop to be active when loading. */
|
|
199
|
+
onStop?: () => void;
|
|
200
|
+
/** Hard character cap. */
|
|
201
|
+
maxLength?: number;
|
|
202
|
+
/** Auto-focus the editor on mount. Default false. */
|
|
203
|
+
autoFocus?: boolean;
|
|
204
|
+
/** Whether Enter submits. Default true (Shift-Enter still inserts a newline). */
|
|
205
|
+
submitOnEnter?: boolean;
|
|
206
|
+
/**
|
|
207
|
+
* Custom content for the left action slot. Replaces the default
|
|
208
|
+
* paperclip button when `attachments` is enabled.
|
|
209
|
+
*/
|
|
210
|
+
leftActions?: React.ReactNode;
|
|
211
|
+
/**
|
|
212
|
+
* Custom content for the right action slot. Replaces the default
|
|
213
|
+
* Send / Stop button. Use the `useComposer()` hook inside to access
|
|
214
|
+
* imperative methods.
|
|
215
|
+
*/
|
|
216
|
+
rightActions?: React.ReactNode;
|
|
217
|
+
/** Hide the default Send button without replacing it. */
|
|
218
|
+
hideSend?: boolean;
|
|
219
|
+
/** Scripted demo steps. */
|
|
220
|
+
steps?: ComposerStep[];
|
|
221
|
+
/** What kicks the script off. Defaults to "mount". */
|
|
222
|
+
trigger?: DemoTrigger;
|
|
223
|
+
/** For trigger="manual" — flip true to play. */
|
|
224
|
+
play?: boolean;
|
|
225
|
+
/** Animation feel. */
|
|
226
|
+
speed?: DemoSpeed;
|
|
227
|
+
/** Loop the script forever. */
|
|
228
|
+
loop?: boolean;
|
|
229
|
+
/**
|
|
230
|
+
* Pause between loop iterations (ms). Defaults to 2000. Marketing
|
|
231
|
+
* heroes that want the demo to breathe between repeats bump this
|
|
232
|
+
* higher; tight inline demos drop it.
|
|
233
|
+
*/
|
|
234
|
+
loopDelay?: number;
|
|
235
|
+
/**
|
|
236
|
+
* Fires once per loop cycle, AFTER the loopDelay pause and BEFORE
|
|
237
|
+
* the script replays. Use to reset parent state that the script
|
|
238
|
+
* mutated via onSubmit (e.g., wipe a messages list back to its
|
|
239
|
+
* seed before the demo types into it again). The editor is cleared
|
|
240
|
+
* automatically — you only need this hook if state outside the
|
|
241
|
+
* Composer needs to reset too.
|
|
242
|
+
*/
|
|
243
|
+
onLoopReset?: () => void;
|
|
244
|
+
/**
|
|
245
|
+
* Bare mode — strip the card chrome (border / bg / rounding). Use
|
|
246
|
+
* when embedding inside an existing card or column layout.
|
|
247
|
+
*/
|
|
248
|
+
bare?: boolean;
|
|
249
|
+
/**
|
|
250
|
+
* Read-only mode — disables editing AND focusability. Programmatic
|
|
251
|
+
* updates (including scripted demo playback) still work. Use for
|
|
252
|
+
* marketing surfaces that render a Composer purely for show, so the
|
|
253
|
+
* scripted typing doesn't steal focus from other inputs on the page.
|
|
254
|
+
* Hides the default Send / paperclip action row.
|
|
255
|
+
*/
|
|
256
|
+
readOnly?: boolean;
|
|
257
|
+
className?: string;
|
|
258
|
+
}
|
|
259
|
+
declare const Composer: React.ForwardRefExoticComponent<ComposerProps & React.RefAttributes<ComposerHandle>>;
|
|
260
|
+
declare const ComposerReply: React.ForwardRefExoticComponent<ComposerProps & React.RefAttributes<ComposerHandle>>;
|
|
261
|
+
|
|
262
|
+
export { Composer, type ComposerAttachment, type ComposerAttachmentConfig, type ComposerContent, type ComposerFormat, type ComposerHandle, type ComposerMentionItem, type ComposerProps, ComposerReply, type ComposerStep, type ComposerTriggerConfig };
|
package/dist/composer.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';var e=require('react'),LexicalComposer=require('@lexical/react/LexicalComposer'),LexicalRichTextPlugin=require('@lexical/react/LexicalRichTextPlugin'),LexicalContentEditable=require('@lexical/react/LexicalContentEditable'),LexicalErrorBoundary=require('@lexical/react/LexicalErrorBoundary'),LexicalHistoryPlugin=require('@lexical/react/LexicalHistoryPlugin'),LexicalOnChangePlugin=require('@lexical/react/LexicalOnChangePlugin'),LexicalListPlugin=require('@lexical/react/LexicalListPlugin'),LexicalLinkPlugin=require('@lexical/react/LexicalLinkPlugin'),LexicalComposerContext=require('@lexical/react/LexicalComposerContext'),richText=require('@lexical/rich-text'),list=require('@lexical/list'),link=require('@lexical/link'),code=require('@lexical/code'),selection=require('@lexical/selection'),utils=require('@lexical/utils'),lexical=require('lexical'),lexicalBeautifulMentions=require('lexical-beautiful-mentions'),react=require('motion/react'),lucideReact=require('lucide-react'),clsx=require('clsx'),tailwindMerge=require('tailwind-merge');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var e__namespace=/*#__PURE__*/_interopNamespace(e);var pe={slow:{tokenStagger:70,lineStagger:200,preDelay:500,fadeMs:480},normal:{tokenStagger:22,lineStagger:55,preDelay:200,fadeMs:280},fast:{tokenStagger:8,lineStagger:18,preDelay:60,fadeMs:160}},ce=.55;function E(r,t){return r<=0?Promise.resolve():new Promise((o,s)=>{if(t?.aborted){s(new DOMException("Aborted","AbortError"));return}let n=window.setTimeout(()=>{t?.removeEventListener("abort",a),o();},r),a=()=>{window.clearTimeout(n),s(new DOMException("Aborted","AbortError"));};t?.addEventListener("abort",a,{once:true});})}async function ue(r,t,o=22,s){if(o<=0){t(r);return}for(let n=1;n<=r.length;n++)t(r.slice(0,n)),n<r.length&&await E(o,s);}function de(r){return r instanceof DOMException&&r.name==="AbortError"}var fe="data-motion",we="off",ge="(prefers-reduced-motion: reduce)";function Te(){if(typeof window>"u"||typeof document>"u")return false;let r=window.matchMedia(ge).matches,t=document.documentElement.getAttribute(fe)===we;return r||t}function be(){let[r,t]=e__namespace.useState(false);return e__namespace.useEffect(()=>{let o=()=>t(Te());o();let s=window.matchMedia(ge);s.addEventListener("change",o);let n=new MutationObserver(o);return n.observe(document.documentElement,{attributes:true,attributeFilter:[fe]}),()=>{s.removeEventListener("change",o),n.disconnect();}},[]),r}function he(){let[r,t]=e__namespace.useState(true);return e__namespace.useEffect(()=>{let o=window.self!==window.top,s=()=>document.visibilityState!=="hidden"&&(o||document.hasFocus()),n=()=>t(s());return n(),document.addEventListener("visibilitychange",n),window.addEventListener("focus",n),window.addEventListener("blur",n),window.addEventListener("pageshow",n),()=>{document.removeEventListener("visibilitychange",n),window.removeEventListener("focus",n),window.removeEventListener("blur",n),window.removeEventListener("pageshow",n);}},[]),r}var Se=2e3,De={tokenStagger:0,lineStagger:0,preDelay:0,fadeMs:0};function me(r){let{steps:t,interpret:o,speed:s="normal",trigger:n="mount",play:a,loop:d=false,loopDelay:h=Se,maxLoops:m=1/0,containerRef:R,onComplete:N,onLoopReset:L}=r,p=be(),y=p?De:pe[s],k=p?false:d,A=he(),[I,x]=e__namespace.useState(false),[X,V]=e__namespace.useState(false),[G,q]=e__namespace.useState(-1),$=e__namespace.useRef(null),W=react.useInView(R??$,{once:true,amount:ce}),M=react.useInView(R??$,{amount:ce}),K=A&&(R?M:true),[c,w]=e__namespace.useState(0),f=e__namespace.useMemo(()=>n==="mount"?true:n==="inView"?W:!!a||c>0,[n,W,a,c]),O=e__namespace.useRef(o);O.current=o;let _=e__namespace.useRef(N);_.current=N;let l=e__namespace.useRef(L);l.current=L;let P=e__namespace.useRef(null),T=e__namespace.useRef(null),te=e__namespace.useCallback(()=>{P.current?.abort(),P.current=null,T.current!==null&&(window.clearTimeout(T.current),T.current=null),x(false);},[]),se=e__namespace.useCallback(()=>{w(S=>S+1);},[]),oe=e__namespace.useCallback((S=0)=>{if(P.current?.abort(),P.current=null,T.current!==null&&(window.clearTimeout(T.current),T.current=null),S>0){T.current=window.setTimeout(()=>{T.current=null,w(C=>C+1);},S);return}w(C=>C+1);},[]);return e__namespace.useEffect(()=>()=>{T.current!==null&&(window.clearTimeout(T.current),T.current=null);},[]),e__namespace.useEffect(()=>{if(!t||t.length===0||!p&&(!f||!K))return;let S=new AbortController;P.current=S;let{signal:C}=S,re={speed:y,signal:C,cancelled:()=>C.aborted,reduced:p},ne=true,i=0;return (async()=>{do{x(true),V(false),q(-1);try{await E(n==="inView"?y.preDelay:0,C);for(let u=0;u<t.length;u++){if(C.aborted)return;q(u),await O.current(t[u],re);}if(C.aborted)return;if(q(-1),V(!0),_.current?.(),i+=1,!k||i>=m){x(!1);return}if(await E(h,C),C.aborted)return;l.current?.();}catch(u){if(de(u))return;process.env.NODE_ENV!=="production"&&console.error("[useScriptedDemo] step error:",u),x(false);return}}while(ne&&k&&!C.aborted)})(),()=>{ne=false,S.abort(),P.current===S&&(P.current=null);}},[t,f,K,k,h,y,n,c,p]),{isPlaying:I,isComplete:X,currentIndex:G,play:se,stop:te,restart:oe}}function j(...r){return tailwindMerge.twMerge(clsx.clsx(r))}function Rt({onSubmit:r,enabled:t}){let[o]=LexicalComposerContext.useLexicalComposerContext();return e__namespace.useEffect(()=>{if(t)return o.registerCommand(lexical.KEY_ENTER_COMMAND,s=>s&&s.shiftKey?false:(s?.preventDefault(),r(),true),lexical.COMMAND_PRIORITY_HIGH)},[o,t,r]),null}function vt({onImageFiles:r,enabled:t}){let[o]=LexicalComposerContext.useLexicalComposerContext();return e__namespace.useEffect(()=>{if(!t)return;let s=o.getRootElement();if(!s)return;let n=a=>{let h=Array.from(a.clipboardData?.items??[]).filter(m=>m.kind==="file"&&m.type.startsWith("image/")).map(m=>m.getAsFile()).filter(m=>m!==null);h.length>0&&(a.preventDefault(),r(h));};return s.addEventListener("paste",n),()=>s.removeEventListener("paste",n)},[o,t,r]),null}function Ct({enabled:r}){let[t]=LexicalComposerContext.useLexicalComposerContext();return e__namespace.useEffect(()=>{r&&t.focus();},[t,r]),null}var Re={bold:"bold",italic:"italic",underline:"underline",strikethrough:"strikethrough",code:"code"};function ve(r,t){let o=Re[t];if(o){r.dispatchCommand(lexical.FORMAT_TEXT_COMMAND,o);return}if(t==="ul"){r.dispatchCommand(list.INSERT_UNORDERED_LIST_COMMAND,void 0);return}if(t==="ol"){r.dispatchCommand(list.INSERT_ORDERED_LIST_COMMAND,void 0);return}r.update(()=>{let s=lexical.$getSelection();if(lexical.$isRangeSelection(s)){if(t==="h1"||t==="h2"||t==="h3"){let n=t;selection.$setBlocksType(s,()=>richText.$createHeadingNode(n));return}(t==="blockquote"||t==="pullquote")&&selection.$setBlocksType(s,()=>{let n=richText.$createQuoteNode();return t==="pullquote"&&(n.__pullquote=true),n});}});}function Et(r,t,o,s){r.update(()=>{let n=lexical.$getSelection();if(!lexical.$isRangeSelection(n))return;let a=lexicalBeautifulMentions.$createBeautifulMentionNode(t,o,s);n.insertNodes([a]),n.insertText(" ");});}function wt(r,t){let o=false;return r.update(()=>{let s=lexical.$getRoot(),a=s.getTextContent().indexOf(t);if(a===-1)return;let d=0,h=null,m=0,R=null,N=0,L=p=>{if(!(h&&R)){if(p.getType()==="text"){let y=p,k=y.getTextContentSize(),A=d,I=d+k;!h&&a>=A&&a<I&&(h=y,m=a-A);let x=a+t.length;!R&&x>A&&x<=I&&(R=y,N=x-A),d=I;return}if("getChildren"in p)for(let y of p.getChildren())L(y);else d+=p.getTextContentSize();}};if(L(s),h&&R){let p=h,y=R;p.select(m,0).focus.set(y.getKey(),N,"text"),o=true;}}),o}function Z(r){r.update(()=>{let t=lexical.$getRoot();t.clear();let o=lexical.$createParagraphNode();t.append(o),o.select();});}function Ce(r){let t="",o="",s=[];return r.getEditorState().read(()=>{t=lexical.$getRoot().getTextContent(),o=JSON.stringify(r.getEditorState().toJSON());let n=a=>{if(a.getType()==="beautifulMention"){let d=a;s.push({trigger:d.getTrigger(),value:d.getValue(),data:d.getData()});}if("getChildren"in a)for(let d of a.getChildren())n(d);};n(lexical.$getRoot());}),{text:t,json:o,mentions:s}}var Tt=[{format:"bold",icon:lucideReact.Bold,label:"Bold"},{format:"italic",icon:lucideReact.Italic,label:"Italic"},{format:"underline",icon:lucideReact.Underline,label:"Underline"},{format:"strikethrough",icon:lucideReact.Strikethrough,label:"Strikethrough"},{format:"code",icon:lucideReact.Code,label:"Inline code"},{format:"h1",icon:lucideReact.Heading1,label:"Heading 1"},{format:"h2",icon:lucideReact.Heading2,label:"Heading 2"},{format:"h3",icon:lucideReact.Heading3,label:"Heading 3"},{format:"blockquote",icon:lucideReact.Quote,label:"Blockquote"},{format:"ul",icon:lucideReact.List,label:"Bulleted list"},{format:"ol",icon:lucideReact.ListOrdered,label:"Numbered list"}];function St({formats:r}){let[t]=LexicalComposerContext.useLexicalComposerContext(),[o,s]=e__namespace.useState(()=>new Set);e__namespace.useEffect(()=>utils.mergeRegister(t.registerUpdateListener(({editorState:a})=>{a.read(()=>{let d=lexical.$getSelection();if(!lexical.$isRangeSelection(d))return;let h=new Set;for(let m of ["bold","italic","underline","strikethrough","code"])d.hasFormat(m)&&h.add(m);s(h);});})),[t]);let n=Tt.filter(a=>r.includes(a.format));return e__namespace.createElement("div",{"data-gds-part":"composer-toolbar",className:j("flex flex-wrap items-center gap-0.5","px-2 py-1")},n.map(({format:a,icon:d,label:h})=>{let m=Re[a],R=m?o.has(m):false;return e__namespace.createElement("button",{key:a,type:"button","aria-label":h,title:h,"data-gds-part":"composer-toolbar-button","data-gds-active":R?"true":"false",onClick:()=>ve(t,a),className:j("h-7 w-7 inline-flex items-center justify-center rounded","text-[var(--gds-composer-toolbar-fg)]","hover:bg-[var(--gds-composer-toolbar-hover-bg)]","focus:outline-none focus:ring-2 focus:ring-primary",R&&"bg-[var(--gds-composer-toolbar-active-bg)] text-[var(--gds-composer-toolbar-active-fg)]")},e__namespace.createElement(d,{className:"h-3.5 w-3.5"}))}))}function Dt({attachments:r,onRemove:t}){return e__namespace.createElement(react.AnimatePresence,{initial:false},r.length>0&&e__namespace.createElement(react.motion.div,{initial:{opacity:0,height:0},animate:{opacity:1,height:"auto"},exit:{opacity:0,height:0},transition:{duration:.18},className:"overflow-hidden"},e__namespace.createElement("div",{"data-gds-part":"composer-attachments",className:"flex flex-wrap gap-2 px-2 py-2"},r.map(o=>e__namespace.createElement("div",{key:o.id,className:"relative group"},o.file.type.startsWith("image/")?e__namespace.createElement("img",{src:o.previewUrl,alt:o.name,className:"h-14 w-14 rounded-md object-cover border border-[var(--gds-composer-border)]"}):e__namespace.createElement("div",{className:"h-14 w-14 rounded-md border border-[var(--gds-composer-border)] flex items-center justify-center text-xs px-1 text-center text-[var(--gds-composer-muted-fg)]"},o.name.slice(0,18)),e__namespace.createElement("button",{type:"button",onClick:()=>t(o.id),"aria-label":`Remove ${o.name}`,className:j("absolute -top-1.5 -right-1.5 h-5 w-5 rounded-full","bg-[var(--gds-composer-chip-remove-bg)]","text-[var(--gds-composer-chip-remove-fg)]","flex items-center justify-center","opacity-0 group-hover:opacity-100 focus:opacity-100","focus:outline-none focus:ring-2 focus:ring-primary","transition-opacity")},e__namespace.createElement(lucideReact.X,{className:"w-3 h-3"})))))))}function Nt({placeholder:r,triggers:t,attachmentsCfg:o,formatList:s,showToolbar:n,isLoading:a,onStop:d,maxLength:h,autoFocus:m,submitOnEnter:R=true,leftActions:N,rightActions:L,hideSend:p,steps:y,trigger:k="mount",play:A,speed:I="normal",loop:x=false,loopDelay:X,onLoopReset:V,bare:G,className:q,onSubmit:$,onChange:W,readOnly:M,handleEditorReady:K,submitRef:c,restartRef:w,attachments:f,setAttachments:O,ingestImages:_}){let[l]=LexicalComposerContext.useLexicalComposerContext(),P=e__namespace.useRef(null),T=e__namespace.useRef(null),[te,se]=e__namespace.useState(false);e__namespace.useEffect(()=>{K(l);},[l,K]),e__namespace.useEffect(()=>{l.setEditable(!M);},[l,M]);let oe=e__namespace.useRef(W);oe.current=W,e__namespace.useEffect(()=>l.registerUpdateListener(({editorState:i})=>{i.read(()=>{let v=lexical.$getRoot().getTextContent();se(v.trim().length>0),oe.current?.(v);});}),[l]);let S=e__namespace.useCallback(()=>{if(a)return;let i=Ce(l),v=i.text.trim().length>0,u=f.length>0;!v&&!u||($?.(i,u?f:void 0),Z(l),f.forEach(J=>URL.revokeObjectURL(J.previewUrl)),O([]));},[l,a,f,$,O]);c.current=S;let{restart:C}=me({steps:y,speed:I,trigger:k,play:A,loop:x,loopDelay:X,containerRef:P,onLoopReset:()=>{Z(l),V?.();},interpret:async(i,v)=>{let u=v.signal;if(i.type==="wait")return E(i.ms,u);if(i.type==="clear")return Z(l),E(120,u);if(i.type==="newline")return l.update(()=>{let g=lexical.$getSelection();lexical.$isRangeSelection(g)&&g.insertParagraph();}),E(60,u);if(i.type==="submit")return S(),E(120,u);if(i.type==="format")return ve(l,i.format),l.update(()=>{let g=lexical.$getSelection();if(!lexical.$isRangeSelection(g)||g.isCollapsed())return;let D=g.focus;g.anchor.set(D.key,D.offset,D.type);}),E(80,u);if(i.type==="select")return wt(l,i.text),E(120,u);if(i.type==="mention")return i.query&&(l.update(()=>{let g=lexical.$getSelection();lexical.$isRangeSelection(g)&&g.insertText(i.trigger);}),await ue(i.query,g=>{l.update(()=>{let D=lexical.$getRoot(),Y=D.getTextContent(),B=Y.slice(0,Y.length-i.trigger.length-(g.length-1));D.clear();let Q=lexical.$createParagraphNode();B&&Q.append(lexical.$createTextNode(B)),Q.append(lexical.$createTextNode(i.trigger+g)),D.append(Q);});},v.speed.tokenStagger,u),await E(180,u),l.update(()=>{let g=lexical.$getRoot(),D=g.getTextContent(),Y=i.trigger.length+i.query.length,B=D.slice(0,D.length-Y);g.clear();let Q=lexical.$createParagraphNode();B&&Q.append(lexical.$createTextNode(B)),g.append(Q),Q.select();})),Et(l,i.trigger,i.value),E(120,u);let J=i.speed?{slow:70,normal:22,fast:8}[i.speed]:v.speed.tokenStagger;for(let g=0;g<i.text.length;g++){if(u.aborted)return;let D=i.text[g];l.update(()=>{let Y=lexical.$getSelection();if(!lexical.$isRangeSelection(Y)){lexical.$getRoot().selectEnd();let B=lexical.$getSelection();lexical.$isRangeSelection(B)&&B.insertText(D);return}Y.insertText(D);}),g<i.text.length-1&&await E(J,u);}}});e__namespace.useEffect(()=>{w.current=i=>{Z(l),C(i);};},[l,C,w]);let le=!M&&!L&&!p,re=!M&&!N&&o!==null,ne=!M&&(re||N||le||L);return e__namespace.createElement("div",{ref:P,"data-gds-part":"composer","data-gds-loading":a?"true":"false",className:j("w-full",!G&&["rounded-md","bg-transparent","border border-input","shadow-sm","focus-within:outline-none focus-within:ring-1 focus-within:ring-ring","transition-colors"],q)},n&&e__namespace.createElement(St,{formats:s}),o&&e__namespace.createElement(Dt,{attachments:f,onRemove:i=>{O(v=>{let u=v.find(J=>J.id===i);return u&&URL.revokeObjectURL(u.previewUrl),v.filter(J=>J.id!==i)});}}),e__namespace.createElement("div",{className:"relative"},e__namespace.createElement(LexicalRichTextPlugin.RichTextPlugin,{contentEditable:e__namespace.createElement(LexicalContentEditable.ContentEditable,{"data-gds-part":"composer-editor",className:j("outline-none","px-3 sm:px-4 py-3","text-sm text-[var(--gds-composer-fg)]","min-h-[44px] max-h-[300px] overflow-y-auto","[&_p]:m-0 [&_p+p]:mt-2","[&_h1]:text-xl [&_h1]:font-semibold [&_h1]:mt-2","[&_h2]:text-lg [&_h2]:font-semibold [&_h2]:mt-2","[&_h3]:text-base [&_h3]:font-semibold [&_h3]:mt-2","[&_ul]:list-disc [&_ul]:pl-5 [&_ol]:list-decimal [&_ol]:pl-5","[&_blockquote]:border-l-2 [&_blockquote]:border-[var(--gds-composer-border)] [&_blockquote]:pl-3 [&_blockquote]:italic","[&_code]:bg-[var(--gds-composer-toolbar-active-bg)] [&_code]:px-1 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-xs [&_code]:font-mono",a&&"opacity-60 pointer-events-none"),"aria-placeholder":r??"",placeholder:e__namespace.createElement("div",{"data-gds-part":"composer-placeholder",className:"absolute top-3 left-3 sm:left-4 text-sm text-muted-foreground pointer-events-none select-none"},r),spellCheck:true}),ErrorBoundary:LexicalErrorBoundary.LexicalErrorBoundary}),e__namespace.createElement(LexicalHistoryPlugin.HistoryPlugin,null),e__namespace.createElement(LexicalListPlugin.ListPlugin,null),e__namespace.createElement(LexicalLinkPlugin.LinkPlugin,null),e__namespace.createElement(LexicalOnChangePlugin.OnChangePlugin,{onChange:()=>{}}),e__namespace.createElement(Ct,{enabled:!!m}),e__namespace.createElement(Rt,{onSubmit:S,enabled:R}),o&&e__namespace.createElement(vt,{onImageFiles:_,enabled:true}),t.length>0&&e__namespace.createElement(lexicalBeautifulMentions.BeautifulMentionsPlugin,{items:t.reduce((i,v)=>(typeof v.items=="function"||(i[v.char]=v.items.map(u=>({value:u.value}))),i),{})})),ne&&e__namespace.createElement("div",{"data-gds-part":"composer-actions",className:"flex items-center justify-between gap-2 px-2 pb-2 pt-1"},e__namespace.createElement("div",{className:"flex items-center gap-1"},N??(re&&e__namespace.createElement("button",{type:"button",onClick:()=>T.current?.click(),disabled:a,"aria-label":"Attach image",title:"Attach image",className:j("h-8 w-8 rounded-lg flex items-center justify-center","text-[var(--gds-composer-action-fg)]","hover:text-[var(--gds-composer-fg)]","hover:bg-[var(--gds-composer-toolbar-hover-bg)]","focus:outline-none focus:ring-2 focus:ring-primary","transition-colors","disabled:opacity-50 disabled:cursor-not-allowed")},e__namespace.createElement(lucideReact.Paperclip,{className:"w-4 h-4"})))),L??(le&&e__namespace.createElement("button",{type:"button",onClick:a?d:S,disabled:a?!d:!te&&f.length===0,"aria-label":a?"Stop":"Send",className:j("h-8 w-8 rounded-lg flex items-center justify-center transition-colors flex-shrink-0","focus:outline-none focus:ring-2 focus:ring-primary",a?"bg-red-500 hover:bg-red-600 text-white disabled:opacity-50":te||f.length>0?"bg-primary hover:bg-primary/90 text-primary-foreground":"bg-[var(--gds-composer-toolbar-active-bg)] text-[var(--gds-composer-muted-fg)] cursor-not-allowed")},a?e__namespace.createElement(lucideReact.Square,{className:"w-3.5 h-3.5"}):e__namespace.createElement(lucideReact.Send,{className:"w-3.5 h-3.5"}))),o&&e__namespace.createElement("input",{ref:T,type:"file",accept:o.accept,multiple:o.multiple,onChange:i=>{i.target.files&&_(Array.from(i.target.files)),i.target.value="";},className:"sr-only",tabIndex:-1,"aria-hidden":"true"})))}var Ee=e__namespace.forwardRef(function(t,o){let{initialText:s,initialJson:n,formats:a,toolbar:d,triggers:h,attachments:m,...R}=t,N=e__namespace.useMemo(()=>a===false?[]:a??["bold","italic","underline","strikethrough","code","h1","h2","blockquote","ul","ol"],[a]),L=d===true||d==="top",p=e__namespace.useMemo(()=>{if(!m)return null;let c=m===true?{}:m;return c.enabled===false?null:{enabled:true,accept:c.accept??"image/*",maxItems:c.maxItems??10,multiple:c.multiple??true}},[m]),[y,k]=e__namespace.useState([]),A=e__namespace.useRef(y);A.current=y,e__namespace.useEffect(()=>()=>{A.current.forEach(c=>URL.revokeObjectURL(c.previewUrl));},[]);let I=e__namespace.useCallback(c=>{if(!p)return;let w=c.filter(f=>p.accept?p.accept==="image/*"?f.type.startsWith("image/"):p.accept.split(",").some(O=>{let _=O.trim();return _.endsWith("/*")?f.type.startsWith(_.slice(0,-1)):f.type===_||f.name.toLowerCase().endsWith(_)}):true);w.length!==0&&k(f=>{let O=p.maxItems-f.length;if(O<=0)return f;let _=w.slice(0,O).map(l=>({id:`${l.name}-${l.size}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,file:l,previewUrl:URL.createObjectURL(l),name:l.name}));return [...f,..._]});},[p]),x=e__namespace.useRef(null),X=e__namespace.useRef(()=>{}),V=e__namespace.useRef(()=>{}),G=e__namespace.useCallback(c=>{x.current=c;},[]);e__namespace.useImperativeHandle(o,()=>({play:c=>{console.warn('[Composer] handle.play(steps) is not wired in v1 \u2014 pass `steps` + `trigger="manual"` and toggle `play` instead.');},stop:()=>{},restart:c=>V.current(c),focus:()=>x.current?.focus(),clear:()=>{let c=x.current;c&&Z(c);},insert:c=>{x.current?.update(()=>{let w=lexical.$getSelection();lexical.$isRangeSelection(w)&&w.insertText(c);});},getContent:()=>x.current?Ce(x.current):{text:"",json:"",mentions:[]},getEditor:()=>x.current}),[]);let q=e__namespace.useMemo(()=>({paragraph:"gds-composer-paragraph",quote:"gds-composer-quote",heading:{h1:"gds-composer-h1",h2:"gds-composer-h2",h3:"gds-composer-h3"},list:{ul:"gds-composer-ul",ol:"gds-composer-ol",listitem:"gds-composer-li"},text:{bold:"font-semibold",italic:"italic",underline:"underline",strikethrough:"line-through",code:"gds-composer-code"},beautifulMentions:{"@":"gds-composer-mention px-1.5 py-0.5 mx-0.5 rounded bg-primary/10 text-primary font-medium","/":"gds-composer-mention px-1.5 py-0.5 mx-0.5 rounded bg-violet-500/15 text-violet-600 dark:text-violet-400 font-medium"}}),[]),$=e__namespace.useMemo(()=>({namespace:"gds-composer",theme:q,onError:c=>{console.error("[Composer]",c);},nodes:[richText.HeadingNode,richText.QuoteNode,list.ListNode,list.ListItemNode,link.LinkNode,link.AutoLinkNode,code.CodeNode,code.CodeHighlightNode,lexicalBeautifulMentions.BeautifulMentionNode],editorState:n||(s?c=>{let w=lexical.$getRoot(),f=lexical.$createParagraphNode();f.append(lexical.$createTextNode(s)),w.append(f);}:void 0)}),[q,n,s]),W=N.length>0,{readOnly:M=false,...K}=R;return e__namespace.createElement(LexicalComposer.LexicalComposer,{initialConfig:{...$,editable:!M}},e__namespace.createElement(Nt,{...K,readOnly:M,triggers:h??[],attachmentsCfg:M?null:p,formatList:N,showToolbar:L&&W&&!M,handleEditorReady:G,submitRef:X,restartRef:V,attachments:y,setAttachments:k,ingestImages:I}))}),Mt=e__namespace.forwardRef(function(t,o){return e__namespace.createElement(Ee,{ref:o,placeholder:"Write a reply\u2026",formats:false,submitOnEnter:false,...t})});exports.Composer=Ee;exports.ComposerReply=Mt;//# sourceMappingURL=composer.js.map
|
|
3
|
+
//# sourceMappingURL=composer.js.map
|