@marianmeres/stuic 3.134.0 → 3.136.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/dist/components/CommentInput/CommentInput.svelte +186 -348
- package/dist/components/CommentInput/CommentInput.svelte.d.ts +42 -44
- package/dist/components/CommentInput/README.md +104 -77
- package/dist/components/CommentInput/index.css +29 -259
- package/dist/components/CommentInput/index.d.ts +1 -2
- package/dist/components/CommentInput/index.js +1 -1
- package/dist/components/Input/FieldAssets.svelte +179 -96
- package/dist/components/Input/FieldAssets.svelte.d.ts +9 -0
- package/dist/components/Input/README.md +23 -3
- package/dist/components/MarkdownEditor/MarkdownEditor.svelte +10 -0
- package/dist/components/MarkdownEditor/MarkdownEditor.svelte.d.ts +1 -0
- package/package.json +1 -1
- package/dist/components/CommentInput/_internal/render.d.ts +0 -24
- package/dist/components/CommentInput/_internal/render.js +0 -47
|
@@ -1,65 +1,79 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
|
-
import type {
|
|
4
|
-
|
|
3
|
+
import type {
|
|
4
|
+
ValidateOptions,
|
|
5
|
+
ValidationResult,
|
|
6
|
+
} from "../../actions/validate.svelte.js";
|
|
5
7
|
import type { THC } from "../Thc/Thc.svelte";
|
|
6
8
|
import type { InputWrapClassProps } from "../Input/types.js";
|
|
7
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
MarkdownEditorMode,
|
|
11
|
+
ToolbarItem,
|
|
12
|
+
PromptFn,
|
|
13
|
+
} from "../MarkdownEditor/index.js";
|
|
8
14
|
|
|
9
15
|
type SnippetWithId = Snippet<[{ id: string }]>;
|
|
10
16
|
|
|
11
|
-
/** Active
|
|
12
|
-
export type CommentInputMode =
|
|
17
|
+
/** Active editing surface of the comment box (mirrors `MarkdownEditor`). */
|
|
18
|
+
export type CommentInputMode = MarkdownEditorMode;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Minimal default toolbar for the comment box — kept intentionally light
|
|
22
|
+
* (GitHub-composer feel). Pass your own `toolbar` array to customize.
|
|
23
|
+
*/
|
|
24
|
+
export const DEFAULT_COMMENT_TOOLBAR: ToolbarItem[] = [
|
|
25
|
+
"bold",
|
|
26
|
+
"italic",
|
|
27
|
+
"link",
|
|
28
|
+
"codeBlock",
|
|
29
|
+
];
|
|
13
30
|
|
|
14
|
-
export interface Props
|
|
15
|
-
|
|
16
|
-
Omit<HTMLTextareaAttributes, "value" | "class" | "style">,
|
|
17
|
-
InputWrapClassProps {
|
|
18
|
-
/** Comment text (raw markdown). Bindable. */
|
|
31
|
+
export interface Props extends InputWrapClassProps {
|
|
32
|
+
/** Comment content (markdown). Bindable. */
|
|
19
33
|
value?: string;
|
|
20
|
-
/** Active
|
|
34
|
+
/** Active editing surface. Bindable. Default `"source"` (opens on raw markdown). */
|
|
21
35
|
mode?: CommentInputMode;
|
|
22
|
-
/** The
|
|
23
|
-
input?:
|
|
24
|
-
/** Name attribute of the
|
|
36
|
+
/** The editor surface wrapper element. Bindable (read). */
|
|
37
|
+
input?: HTMLDivElement;
|
|
38
|
+
/** Name attribute of the hidden input, for native form submission. */
|
|
25
39
|
name?: string;
|
|
26
40
|
//
|
|
27
41
|
label?: SnippetWithId | THC;
|
|
28
42
|
description?: SnippetWithId | THC;
|
|
29
43
|
class?: string;
|
|
30
44
|
id?: string;
|
|
31
|
-
tabindex?: number;
|
|
32
45
|
renderSize?: "sm" | "md" | "lg" | string;
|
|
33
46
|
required?: boolean;
|
|
34
47
|
disabled?: boolean;
|
|
35
48
|
placeholder?: string;
|
|
36
49
|
validate?: boolean | Omit<ValidateOptions, "setValidationResult">;
|
|
37
|
-
/** Trim surrounding whitespace on blur. Default `true`. */
|
|
38
|
-
useTrim?: boolean;
|
|
39
|
-
/** Grow the textarea with its content (up to `max`). Default `true`. */
|
|
40
|
-
useAutogrow?: boolean | { enabled?: boolean; max?: number };
|
|
41
50
|
//
|
|
42
|
-
/** Show the Write/Preview tabs. Default `true`. Set `false` for a plain markdown textarea. */
|
|
43
|
-
showTabs?: boolean;
|
|
44
|
-
/** Label for the "write" tab. Default `"Write"`. */
|
|
45
|
-
writeLabel?: string;
|
|
46
|
-
/** Label for the "preview" tab. Default `"Preview"`. */
|
|
47
|
-
previewLabel?: string;
|
|
48
|
-
/** Show the subtle "Markdown supported" hint in the tab bar. Default `true`. */
|
|
49
|
-
showMarkdownHint?: boolean;
|
|
50
|
-
/** Text of the markdown hint. Default `"Markdown supported"`. */
|
|
51
|
-
markdownHintLabel?: string;
|
|
52
|
-
/** Placeholder shown in the Preview tab when there is nothing to render. */
|
|
53
|
-
previewEmptyLabel?: string;
|
|
54
|
-
/** Text shown in the Preview tab while the renderer is (lazy-)loading. */
|
|
55
|
-
previewLoadingLabel?: string;
|
|
56
51
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* is used (lazy-loaded; requires the optional `marked` / `dompurify` peers).
|
|
52
|
+
* Formatting toolbar. `true` shows {@link DEFAULT_COMMENT_TOOLBAR}, `false`
|
|
53
|
+
* hides it, or pass an ordered array of {@link ToolbarItem}s (`"|"` for a
|
|
54
|
+
* separator). Default {@link DEFAULT_COMMENT_TOOLBAR}.
|
|
61
55
|
*/
|
|
62
|
-
|
|
56
|
+
toolbar?: boolean | ToolbarItem[];
|
|
57
|
+
/** Toolbar used on mobile / touch devices. Same shape as `toolbar`. */
|
|
58
|
+
mobileToolbar?: boolean | ToolbarItem[];
|
|
59
|
+
/** Start in source mode on mobile / touch devices. Default `true`. */
|
|
60
|
+
autoSourceOnMobile?: boolean;
|
|
61
|
+
/** Media query defining "mobile". Default `"(pointer: coarse) and (max-width: 640px)"`. */
|
|
62
|
+
mobileQuery?: string;
|
|
63
|
+
/** URL prompt used by the link/image buttons. Defaults to `window.prompt`. */
|
|
64
|
+
prompt?: PromptFn;
|
|
65
|
+
/** Cap the editing surface height (long content scrolls inside). Default `32rem`. */
|
|
66
|
+
maxHeight?: number | string;
|
|
67
|
+
/** Also cap the surface to the parent's available height. Default `true`. */
|
|
68
|
+
capToParent?: boolean;
|
|
69
|
+
/** Show the WYSIWYG/Source mode toggle. Default `true`. */
|
|
70
|
+
showModeToggle?: boolean;
|
|
71
|
+
/** Toggle label while in WYSIWYG mode (switches to source). Default `"Source"`. */
|
|
72
|
+
sourceLabel?: string;
|
|
73
|
+
/** Toggle label while in source mode (switches to WYSIWYG). Default `"Preview"`. */
|
|
74
|
+
previewLabel?: string;
|
|
75
|
+
/** Wire ⌘/Ctrl+B / I / K to bold / italic / link. Default `true`. */
|
|
76
|
+
useShortcuts?: boolean;
|
|
63
77
|
//
|
|
64
78
|
/**
|
|
65
79
|
* Submit handler. Receives the current value. May be async — the submit
|
|
@@ -78,7 +92,7 @@
|
|
|
78
92
|
showSubmit?: boolean;
|
|
79
93
|
/** Show the cancel button. Defaults to `true` when `onCancel` is set. */
|
|
80
94
|
showCancel?: boolean;
|
|
81
|
-
/** Submit on ⌘/Ctrl+Enter while the
|
|
95
|
+
/** Submit on ⌘/Ctrl+Enter while the editor is focused. Default `true`. */
|
|
82
96
|
submitOnModEnter?: boolean;
|
|
83
97
|
/** Clear the value after a successful submit. Default `true`. */
|
|
84
98
|
clearOnSubmit?: boolean;
|
|
@@ -98,12 +112,10 @@
|
|
|
98
112
|
/** Extra content rendered in the footer, left of the buttons (e.g. attach). */
|
|
99
113
|
footer?: Snippet;
|
|
100
114
|
//
|
|
101
|
-
/** Classes for the
|
|
115
|
+
/** Classes for the editor surface element (forwarded to MarkdownEditor). */
|
|
102
116
|
classInput?: string;
|
|
103
|
-
/** Classes for the
|
|
117
|
+
/** Classes for the toolbar bar (forwarded to MarkdownEditor's `classToolbar`). */
|
|
104
118
|
classBar?: string;
|
|
105
|
-
/** Classes for the preview panel. */
|
|
106
|
-
classPreview?: string;
|
|
107
119
|
/** Classes for the footer (button row). */
|
|
108
120
|
classFooter?: string;
|
|
109
121
|
style?: string;
|
|
@@ -111,22 +123,15 @@
|
|
|
111
123
|
</script>
|
|
112
124
|
|
|
113
125
|
<script lang="ts">
|
|
114
|
-
import { autogrow } from "../../actions/autogrow.svelte.js";
|
|
115
|
-
import { trim } from "../../actions/trim.svelte.js";
|
|
116
|
-
import {
|
|
117
|
-
validate as validateAction,
|
|
118
|
-
type ValidationResult,
|
|
119
|
-
} from "../../actions/validate.svelte.js";
|
|
120
126
|
import { getId } from "../../utils/get-id.js";
|
|
121
127
|
import { twMerge } from "../../utils/tw-merge.js";
|
|
122
128
|
import { Button } from "../Button/index.js";
|
|
123
129
|
import Thc from "../Thc/Thc.svelte";
|
|
124
|
-
import
|
|
125
|
-
import { renderMarkdownSafe } from "./_internal/render.js";
|
|
130
|
+
import { MarkdownEditor } from "../MarkdownEditor/index.js";
|
|
126
131
|
|
|
127
132
|
let {
|
|
128
133
|
value = $bindable(""),
|
|
129
|
-
mode = $bindable("
|
|
134
|
+
mode = $bindable("source"),
|
|
130
135
|
input = $bindable(),
|
|
131
136
|
name,
|
|
132
137
|
//
|
|
@@ -134,24 +139,23 @@
|
|
|
134
139
|
description,
|
|
135
140
|
class: classProp,
|
|
136
141
|
id = getId(),
|
|
137
|
-
tabindex = 0,
|
|
138
142
|
renderSize = "md",
|
|
139
143
|
required = false,
|
|
140
144
|
disabled = false,
|
|
141
145
|
placeholder,
|
|
142
|
-
// Renamed to avoid collision with the exported `validate()` below.
|
|
143
146
|
validate: validateProp,
|
|
144
|
-
useTrim = true,
|
|
145
|
-
useAutogrow = true,
|
|
146
147
|
//
|
|
147
|
-
|
|
148
|
-
|
|
148
|
+
toolbar = DEFAULT_COMMENT_TOOLBAR,
|
|
149
|
+
mobileToolbar,
|
|
150
|
+
autoSourceOnMobile = true,
|
|
151
|
+
mobileQuery,
|
|
152
|
+
prompt,
|
|
153
|
+
maxHeight,
|
|
154
|
+
capToParent = true,
|
|
155
|
+
showModeToggle = true,
|
|
156
|
+
sourceLabel = "Source",
|
|
149
157
|
previewLabel = "Preview",
|
|
150
|
-
|
|
151
|
-
markdownHintLabel = "Markdown supported",
|
|
152
|
-
previewEmptyLabel = "Nothing to preview",
|
|
153
|
-
previewLoadingLabel = "Loading preview…",
|
|
154
|
-
renderMarkdown,
|
|
158
|
+
useShortcuts = true,
|
|
155
159
|
//
|
|
156
160
|
onSubmit,
|
|
157
161
|
onCancel,
|
|
@@ -176,7 +180,6 @@
|
|
|
176
180
|
//
|
|
177
181
|
classInput,
|
|
178
182
|
classBar,
|
|
179
|
-
classPreview,
|
|
180
183
|
classFooter,
|
|
181
184
|
classLabel,
|
|
182
185
|
classLabelBox,
|
|
@@ -188,109 +191,36 @@
|
|
|
188
191
|
classBelowBox,
|
|
189
192
|
classValidationBox,
|
|
190
193
|
style,
|
|
191
|
-
//
|
|
192
|
-
...rest
|
|
193
194
|
}: Props = $props();
|
|
194
195
|
|
|
195
|
-
//
|
|
196
|
-
let
|
|
197
|
-
const setValidationResult = (res: ValidationResult) => (validation = res);
|
|
198
|
-
let _doValidate: (() => void) | undefined = $state();
|
|
196
|
+
// The embedded editor instance (exposes validate/focus/command/… imperatively).
|
|
197
|
+
let editor = $state<MarkdownEditor>();
|
|
199
198
|
|
|
200
199
|
// --- Submit state ---------------------------------------------------------
|
|
201
200
|
let submitting = $state(false);
|
|
202
201
|
const isEmpty = $derived(!(value ?? "").trim());
|
|
203
|
-
// Resolve the show* defaults from the presence of their handlers.
|
|
204
202
|
const _showSubmit = $derived(showSubmit ?? !!onSubmit);
|
|
205
203
|
const _showCancel = $derived(showCancel ?? !!onCancel);
|
|
206
204
|
const _busy = $derived(disabled || busy || submitting);
|
|
207
205
|
const canSubmit = $derived(!_busy && !(submitDisabledWhenEmpty && isEmpty));
|
|
208
|
-
|
|
209
|
-
//
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
let previewLoading = $state(false);
|
|
213
|
-
|
|
214
|
-
$effect(() => {
|
|
215
|
-
if (mode !== "preview" || !showTabs) return;
|
|
216
|
-
const md = value ?? "";
|
|
217
|
-
if (!md.trim()) {
|
|
218
|
-
previewHtml = "";
|
|
219
|
-
previewError = false;
|
|
220
|
-
previewLoading = false;
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
const render = renderMarkdown ?? renderMarkdownSafe;
|
|
224
|
-
let cancelled = false;
|
|
225
|
-
previewLoading = true;
|
|
226
|
-
// Drop the previous value's HTML so the loading placeholder shows instead of
|
|
227
|
-
// briefly rendering content that no longer matches `value`.
|
|
228
|
-
previewHtml = "";
|
|
229
|
-
previewError = false;
|
|
230
|
-
(async () => {
|
|
231
|
-
try {
|
|
232
|
-
const html = await render(md);
|
|
233
|
-
if (cancelled) return;
|
|
234
|
-
previewHtml = html;
|
|
235
|
-
previewError = false;
|
|
236
|
-
} catch (err) {
|
|
237
|
-
if (cancelled) return;
|
|
238
|
-
previewHtml = "";
|
|
239
|
-
previewError = true;
|
|
240
|
-
// eslint-disable-next-line no-console
|
|
241
|
-
console.warn(
|
|
242
|
-
"[CommentInput] preview renderer failed. When using the default " +
|
|
243
|
-
"renderer, make sure the optional peers `marked` and `dompurify` " +
|
|
244
|
-
"are installed, or pass a `renderMarkdown` function.",
|
|
245
|
-
err
|
|
246
|
-
);
|
|
247
|
-
} finally {
|
|
248
|
-
if (!cancelled) previewLoading = false;
|
|
249
|
-
}
|
|
250
|
-
})();
|
|
251
|
-
return () => {
|
|
252
|
-
cancelled = true;
|
|
253
|
-
};
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
// Returning to the Write tab: re-measure the textarea. autogrow can't size a
|
|
257
|
-
// `display:none` element, so an external value change made while previewing
|
|
258
|
-
// would otherwise leave the textarea at a stale height.
|
|
259
|
-
$effect(() => {
|
|
260
|
-
if (mode !== "write" || !input || !useAutogrow) return;
|
|
261
|
-
const el = input;
|
|
262
|
-
const max =
|
|
263
|
-
typeof useAutogrow === "object" && useAutogrow.max ? useAutogrow.max : 250;
|
|
264
|
-
requestAnimationFrame(() => {
|
|
265
|
-
el.style.height = "auto";
|
|
266
|
-
const cs = getComputedStyle(el);
|
|
267
|
-
const borderY =
|
|
268
|
-
cs.boxSizing === "border-box"
|
|
269
|
-
? parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth)
|
|
270
|
-
: 0;
|
|
271
|
-
el.style.height = Math.min(el.scrollHeight + borderY, max) + "px";
|
|
272
|
-
});
|
|
273
|
-
});
|
|
206
|
+
// Toggling `disabled` on MarkdownEditor remounts its backend, so only feed it
|
|
207
|
+
// the externally-controlled disable (not the transient `submitting`); the
|
|
208
|
+
// footer buttons carry the in-flight state instead.
|
|
209
|
+
const editorDisabled = $derived(disabled || busy);
|
|
274
210
|
|
|
275
211
|
// --- Handlers -------------------------------------------------------------
|
|
276
212
|
async function handleSubmit() {
|
|
277
213
|
if (!canSubmit) return;
|
|
278
214
|
// Validate before invoking the handler — the submit button bypasses any
|
|
279
215
|
// form-level submit check, so enforce it here (surfaces the inline message).
|
|
280
|
-
const res = validate();
|
|
281
|
-
if (res && !res.valid)
|
|
282
|
-
mode = "write";
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
216
|
+
const res = editor?.validate();
|
|
217
|
+
if (res && !res.valid) return;
|
|
285
218
|
try {
|
|
286
219
|
submitting = true;
|
|
287
220
|
await onSubmit?.(value ?? "");
|
|
288
221
|
if (clearOnSubmit) {
|
|
289
222
|
value = "";
|
|
290
|
-
|
|
291
|
-
if (mode === "preview") mode = "write";
|
|
292
|
-
validation = undefined;
|
|
293
|
-
input?.setCustomValidity?.("");
|
|
223
|
+
editor?.clearValidation();
|
|
294
224
|
}
|
|
295
225
|
} finally {
|
|
296
226
|
submitting = false;
|
|
@@ -301,60 +231,52 @@
|
|
|
301
231
|
onCancel?.();
|
|
302
232
|
}
|
|
303
233
|
|
|
234
|
+
// Capture keydown on the wrapper so we intercept the shortcut BEFORE the
|
|
235
|
+
// editor's own keymap (stopPropagation prevents a double-handle / native bold).
|
|
304
236
|
function handleKeydown(e: KeyboardEvent) {
|
|
305
|
-
if (
|
|
237
|
+
if (!(e.metaKey || e.ctrlKey)) return;
|
|
238
|
+
if (submitOnModEnter && e.key === "Enter") {
|
|
306
239
|
e.preventDefault();
|
|
240
|
+
e.stopPropagation();
|
|
307
241
|
handleSubmit();
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (!useShortcuts) return;
|
|
245
|
+
const cmd =
|
|
246
|
+
e.key === "b" || e.key === "B"
|
|
247
|
+
? "bold"
|
|
248
|
+
: e.key === "i" || e.key === "I"
|
|
249
|
+
? "italic"
|
|
250
|
+
: e.key === "k" || e.key === "K"
|
|
251
|
+
? "link"
|
|
252
|
+
: undefined;
|
|
253
|
+
if (cmd) {
|
|
254
|
+
e.preventDefault();
|
|
255
|
+
e.stopPropagation();
|
|
256
|
+
editor?.command(cmd);
|
|
308
257
|
}
|
|
309
258
|
}
|
|
310
259
|
|
|
311
|
-
|
|
312
|
-
if (disabled) return;
|
|
313
|
-
mode = next;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Roving-tabindex keyboard nav for the Write/Preview tablist (automatic
|
|
317
|
-
// activation — Arrow/Home/End move selection and focus together).
|
|
318
|
-
function handleTabKeydown(e: KeyboardEvent) {
|
|
319
|
-
const keys = ["ArrowRight", "ArrowLeft", "ArrowDown", "ArrowUp", "Home", "End"];
|
|
320
|
-
if (!keys.includes(e.key)) return;
|
|
321
|
-
e.preventDefault();
|
|
322
|
-
const next: CommentInputMode =
|
|
323
|
-
e.key === "Home"
|
|
324
|
-
? "write"
|
|
325
|
-
: e.key === "End"
|
|
326
|
-
? "preview"
|
|
327
|
-
: mode === "write"
|
|
328
|
-
? "preview"
|
|
329
|
-
: "write";
|
|
330
|
-
setMode(next);
|
|
331
|
-
if (disabled) return;
|
|
332
|
-
document.getElementById(`${id}-tab-${next}`)?.focus();
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// --- Imperative API (mirrors FieldTextarea) -------------------------------
|
|
260
|
+
// --- Imperative API (delegates to the embedded MarkdownEditor) ------------
|
|
336
261
|
/** Trigger validation now. Renders the inline message if invalid. */
|
|
337
262
|
export function validate(): ValidationResult | undefined {
|
|
338
|
-
|
|
339
|
-
return validation;
|
|
263
|
+
return editor?.validate();
|
|
340
264
|
}
|
|
341
|
-
/** Clear the inline validation message
|
|
265
|
+
/** Clear the inline validation message. */
|
|
342
266
|
export function clearValidation(): void {
|
|
343
|
-
|
|
344
|
-
input?.setCustomValidity?.("");
|
|
267
|
+
editor?.clearValidation();
|
|
345
268
|
}
|
|
346
269
|
/** Current validation state, or undefined if the validator has never run. */
|
|
347
270
|
export function getValidation(): ValidationResult | undefined {
|
|
348
|
-
return
|
|
271
|
+
return editor?.getValidation();
|
|
349
272
|
}
|
|
350
|
-
/** Focus the
|
|
273
|
+
/** Focus the editor surface. */
|
|
351
274
|
export function focus(): void {
|
|
352
|
-
|
|
353
|
-
input?.focus?.();
|
|
275
|
+
editor?.focus();
|
|
354
276
|
}
|
|
355
277
|
/** Scroll the field into view. Defaults to smooth + center. */
|
|
356
278
|
export function scrollIntoView(opts?: ScrollIntoViewOptions): void {
|
|
357
|
-
|
|
279
|
+
editor?.scrollIntoView(opts);
|
|
358
280
|
}
|
|
359
281
|
/** Programmatically run the submit flow (respects the empty/busy guards). */
|
|
360
282
|
export function submit(): void {
|
|
@@ -370,178 +292,94 @@
|
|
|
370
292
|
{/if}
|
|
371
293
|
{/snippet}
|
|
372
294
|
|
|
373
|
-
<
|
|
374
|
-
{
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
{id}
|
|
378
|
-
{label}
|
|
379
|
-
{labelAfter}
|
|
380
|
-
{below}
|
|
381
|
-
{required}
|
|
382
|
-
{disabled}
|
|
383
|
-
{labelLeft}
|
|
384
|
-
{labelLeftWidth}
|
|
385
|
-
{labelLeftBreakpoint}
|
|
386
|
-
{classLabel}
|
|
387
|
-
{classLabelBox}
|
|
388
|
-
{classInputBox}
|
|
389
|
-
{classInputBoxWrap}
|
|
390
|
-
{classInputBoxWrapInvalid}
|
|
391
|
-
{classDescBox}
|
|
392
|
-
{classDescBoxToggle}
|
|
393
|
-
{classBelowBox}
|
|
394
|
-
{classValidationBox}
|
|
395
|
-
{validation}
|
|
295
|
+
<div
|
|
296
|
+
class={twMerge("stuic-comment-input", classProp)}
|
|
297
|
+
data-size={renderSize}
|
|
298
|
+
class:disabled
|
|
396
299
|
{style}
|
|
300
|
+
onkeydowncapture={handleKeydown}
|
|
397
301
|
>
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
{/if}
|
|
302
|
+
{#if avatar}
|
|
303
|
+
<div class="stuic-comment-input-avatar">
|
|
304
|
+
{@render thcOrSnippet(avatar)}
|
|
305
|
+
</div>
|
|
306
|
+
{/if}
|
|
404
307
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
308
|
+
<div class="stuic-comment-input-main">
|
|
309
|
+
<MarkdownEditor
|
|
310
|
+
bind:this={editor}
|
|
311
|
+
bind:value
|
|
312
|
+
bind:mode
|
|
313
|
+
bind:input
|
|
314
|
+
{id}
|
|
315
|
+
{name}
|
|
316
|
+
{label}
|
|
317
|
+
{description}
|
|
318
|
+
{labelAfter}
|
|
319
|
+
{below}
|
|
320
|
+
{required}
|
|
321
|
+
disabled={editorDisabled}
|
|
322
|
+
{placeholder}
|
|
323
|
+
{renderSize}
|
|
324
|
+
validate={validateProp}
|
|
325
|
+
{toolbar}
|
|
326
|
+
{mobileToolbar}
|
|
327
|
+
{autoSourceOnMobile}
|
|
328
|
+
{mobileQuery}
|
|
329
|
+
{prompt}
|
|
330
|
+
{maxHeight}
|
|
331
|
+
{capToParent}
|
|
332
|
+
{showModeToggle}
|
|
333
|
+
{sourceLabel}
|
|
334
|
+
{previewLabel}
|
|
335
|
+
{labelLeft}
|
|
336
|
+
{labelLeftWidth}
|
|
337
|
+
{labelLeftBreakpoint}
|
|
338
|
+
classInput={twMerge("stuic-comment-input-editor", classInput)}
|
|
339
|
+
classToolbar={classBar}
|
|
340
|
+
{classLabel}
|
|
341
|
+
{classLabelBox}
|
|
342
|
+
{classInputBox}
|
|
343
|
+
{classInputBoxWrap}
|
|
344
|
+
{classInputBoxWrapInvalid}
|
|
345
|
+
{classDescBox}
|
|
346
|
+
{classDescBoxToggle}
|
|
347
|
+
{classBelowBox}
|
|
348
|
+
{classValidationBox}
|
|
349
|
+
onChange={(v) => onChange?.(v)}
|
|
350
|
+
/>
|
|
351
|
+
|
|
352
|
+
{#if _showSubmit || _showCancel || footer}
|
|
353
|
+
<div class={twMerge("stuic-comment-input-footer", classFooter)}>
|
|
354
|
+
<div class="stuic-comment-input-footer-start">
|
|
355
|
+
{@render footer?.()}
|
|
356
|
+
</div>
|
|
357
|
+
<div class="stuic-comment-input-footer-end">
|
|
358
|
+
{#if _showCancel}
|
|
359
|
+
<Button
|
|
360
|
+
type="button"
|
|
361
|
+
variant="ghost"
|
|
362
|
+
size={renderSize}
|
|
363
|
+
disabled={submitting}
|
|
364
|
+
onclick={handleCancel}
|
|
413
365
|
>
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
type="button"
|
|
417
|
-
role="tab"
|
|
418
|
-
aria-selected={mode === "write"}
|
|
419
|
-
aria-controls={`${id}-panel-write`}
|
|
420
|
-
tabindex={mode === "write" ? 0 : -1}
|
|
421
|
-
data-active={mode === "write" ? "true" : undefined}
|
|
422
|
-
{disabled}
|
|
423
|
-
onclick={() => setMode("write")}
|
|
424
|
-
onkeydown={handleTabKeydown}
|
|
425
|
-
>
|
|
426
|
-
{writeLabel}
|
|
427
|
-
</button>
|
|
428
|
-
<button
|
|
429
|
-
id={`${id}-tab-preview`}
|
|
430
|
-
type="button"
|
|
431
|
-
role="tab"
|
|
432
|
-
aria-selected={mode === "preview"}
|
|
433
|
-
aria-controls={`${id}-panel-preview`}
|
|
434
|
-
tabindex={mode === "preview" ? 0 : -1}
|
|
435
|
-
data-active={mode === "preview" ? "true" : undefined}
|
|
436
|
-
{disabled}
|
|
437
|
-
onclick={() => setMode("preview")}
|
|
438
|
-
onkeydown={handleTabKeydown}
|
|
439
|
-
>
|
|
440
|
-
{previewLabel}
|
|
441
|
-
</button>
|
|
442
|
-
</div>
|
|
366
|
+
{cancelLabel}
|
|
367
|
+
</Button>
|
|
443
368
|
{/if}
|
|
444
|
-
{#if
|
|
445
|
-
<
|
|
369
|
+
{#if _showSubmit}
|
|
370
|
+
<Button
|
|
371
|
+
type="button"
|
|
372
|
+
intent="primary"
|
|
373
|
+
size={renderSize}
|
|
374
|
+
disabled={!canSubmit}
|
|
375
|
+
spinner={submitting}
|
|
376
|
+
onclick={handleSubmit}
|
|
377
|
+
>
|
|
378
|
+
{submitLabel}
|
|
379
|
+
</Button>
|
|
446
380
|
{/if}
|
|
447
381
|
</div>
|
|
448
|
-
{/if}
|
|
449
|
-
|
|
450
|
-
<div class="stuic-comment-input-surface">
|
|
451
|
-
<div
|
|
452
|
-
class="stuic-comment-input-panel"
|
|
453
|
-
id={`${id}-panel-write`}
|
|
454
|
-
role={showTabs ? "tabpanel" : undefined}
|
|
455
|
-
aria-labelledby={showTabs ? `${id}-tab-write` : undefined}
|
|
456
|
-
hidden={showTabs && mode === "preview"}
|
|
457
|
-
>
|
|
458
|
-
<textarea
|
|
459
|
-
bind:value
|
|
460
|
-
bind:this={input}
|
|
461
|
-
{...rest}
|
|
462
|
-
{id}
|
|
463
|
-
{name}
|
|
464
|
-
{placeholder}
|
|
465
|
-
{tabindex}
|
|
466
|
-
{required}
|
|
467
|
-
disabled={_busy}
|
|
468
|
-
class={twMerge("stuic-comment-input-textarea", classInput)}
|
|
469
|
-
oninput={(e) => onChange?.(e.currentTarget.value)}
|
|
470
|
-
onkeydown={handleKeydown}
|
|
471
|
-
use:trim={() => ({
|
|
472
|
-
enabled: useTrim,
|
|
473
|
-
setValue: (v: string) => (value = v),
|
|
474
|
-
})}
|
|
475
|
-
use:validateAction={() => ({
|
|
476
|
-
enabled: validateProp !== false,
|
|
477
|
-
...(typeof validateProp === "boolean" ? {} : validateProp),
|
|
478
|
-
setValidationResult,
|
|
479
|
-
setDoValidate: (fn) => (_doValidate = fn),
|
|
480
|
-
})}
|
|
481
|
-
use:autogrow={() => ({
|
|
482
|
-
enabled: !!useAutogrow,
|
|
483
|
-
...(typeof useAutogrow === "boolean" ? {} : useAutogrow),
|
|
484
|
-
value,
|
|
485
|
-
})}
|
|
486
|
-
></textarea>
|
|
487
|
-
</div>
|
|
488
|
-
|
|
489
|
-
{#if showTabs && mode === "preview"}
|
|
490
|
-
<div
|
|
491
|
-
class={twMerge(
|
|
492
|
-
"stuic-comment-input-panel stuic-comment-input-preview",
|
|
493
|
-
classPreview
|
|
494
|
-
)}
|
|
495
|
-
id={`${id}-panel-preview`}
|
|
496
|
-
role="tabpanel"
|
|
497
|
-
aria-labelledby={`${id}-tab-preview`}
|
|
498
|
-
>
|
|
499
|
-
{#if previewError}
|
|
500
|
-
<p class="stuic-comment-input-preview-empty">Preview unavailable.</p>
|
|
501
|
-
{:else if !(value ?? "").trim()}
|
|
502
|
-
<p class="stuic-comment-input-preview-empty">{previewEmptyLabel}</p>
|
|
503
|
-
{:else if previewLoading && !previewHtml}
|
|
504
|
-
<p class="stuic-comment-input-preview-empty">{previewLoadingLabel}</p>
|
|
505
|
-
{:else}
|
|
506
|
-
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
507
|
-
{@html previewHtml}
|
|
508
|
-
{/if}
|
|
509
|
-
</div>
|
|
510
|
-
{/if}
|
|
511
382
|
</div>
|
|
512
|
-
|
|
513
|
-
{#if _showSubmit || _showCancel || footer}
|
|
514
|
-
<div class={twMerge("stuic-comment-input-footer", classFooter)}>
|
|
515
|
-
<div class="stuic-comment-input-footer-start">
|
|
516
|
-
{@render footer?.()}
|
|
517
|
-
</div>
|
|
518
|
-
<div class="stuic-comment-input-footer-end">
|
|
519
|
-
{#if _showCancel}
|
|
520
|
-
<Button
|
|
521
|
-
type="button"
|
|
522
|
-
variant="ghost"
|
|
523
|
-
size={renderSize}
|
|
524
|
-
disabled={submitting}
|
|
525
|
-
onclick={handleCancel}
|
|
526
|
-
>
|
|
527
|
-
{cancelLabel}
|
|
528
|
-
</Button>
|
|
529
|
-
{/if}
|
|
530
|
-
{#if _showSubmit}
|
|
531
|
-
<Button
|
|
532
|
-
type="button"
|
|
533
|
-
intent="primary"
|
|
534
|
-
size={renderSize}
|
|
535
|
-
disabled={!canSubmit}
|
|
536
|
-
spinner={submitting}
|
|
537
|
-
onclick={handleSubmit}
|
|
538
|
-
>
|
|
539
|
-
{submitLabel}
|
|
540
|
-
</Button>
|
|
541
|
-
{/if}
|
|
542
|
-
</div>
|
|
543
|
-
</div>
|
|
544
|
-
{/if}
|
|
545
|
-
</div>
|
|
383
|
+
{/if}
|
|
546
384
|
</div>
|
|
547
|
-
</
|
|
385
|
+
</div>
|