@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.
@@ -1,61 +1,62 @@
1
1
  import type { Snippet } from "svelte";
2
- import type { HTMLTextareaAttributes } from "svelte/elements";
3
- import type { ValidateOptions } from "../../actions/validate.svelte.js";
2
+ import type { ValidateOptions, ValidationResult } from "../../actions/validate.svelte.js";
4
3
  import type { THC } from "../Thc/Thc.svelte";
5
4
  import type { InputWrapClassProps } from "../Input/types.js";
6
- import type { MarkdownRenderer } from "./_internal/render.js";
5
+ import type { MarkdownEditorMode, ToolbarItem, PromptFn } from "../MarkdownEditor/index.js";
7
6
  type SnippetWithId = Snippet<[{
8
7
  id: string;
9
8
  }]>;
10
- /** Active tab of the comment box. */
11
- export type CommentInputMode = "write" | "preview";
12
- export interface Props extends Omit<HTMLTextareaAttributes, "value" | "class" | "style">, InputWrapClassProps {
13
- /** Comment text (raw markdown). Bindable. */
9
+ /** Active editing surface of the comment box (mirrors `MarkdownEditor`). */
10
+ export type CommentInputMode = MarkdownEditorMode;
11
+ /**
12
+ * Minimal default toolbar for the comment box — kept intentionally light
13
+ * (GitHub-composer feel). Pass your own `toolbar` array to customize.
14
+ */
15
+ export declare const DEFAULT_COMMENT_TOOLBAR: ToolbarItem[];
16
+ export interface Props extends InputWrapClassProps {
17
+ /** Comment content (markdown). Bindable. */
14
18
  value?: string;
15
- /** Active tab. Bindable. Default `"write"`. */
19
+ /** Active editing surface. Bindable. Default `"source"` (opens on raw markdown). */
16
20
  mode?: CommentInputMode;
17
- /** The underlying `<textarea>` element. Bindable. */
18
- input?: HTMLTextAreaElement;
19
- /** Name attribute of the textarea, for native form submission. */
21
+ /** The editor surface wrapper element. Bindable (read). */
22
+ input?: HTMLDivElement;
23
+ /** Name attribute of the hidden input, for native form submission. */
20
24
  name?: string;
21
25
  label?: SnippetWithId | THC;
22
26
  description?: SnippetWithId | THC;
23
27
  class?: string;
24
28
  id?: string;
25
- tabindex?: number;
26
29
  renderSize?: "sm" | "md" | "lg" | string;
27
30
  required?: boolean;
28
31
  disabled?: boolean;
29
32
  placeholder?: string;
30
33
  validate?: boolean | Omit<ValidateOptions, "setValidationResult">;
31
- /** Trim surrounding whitespace on blur. Default `true`. */
32
- useTrim?: boolean;
33
- /** Grow the textarea with its content (up to `max`). Default `true`. */
34
- useAutogrow?: boolean | {
35
- enabled?: boolean;
36
- max?: number;
37
- };
38
- /** Show the Write/Preview tabs. Default `true`. Set `false` for a plain markdown textarea. */
39
- showTabs?: boolean;
40
- /** Label for the "write" tab. Default `"Write"`. */
41
- writeLabel?: string;
42
- /** Label for the "preview" tab. Default `"Preview"`. */
43
- previewLabel?: string;
44
- /** Show the subtle "Markdown supported" hint in the tab bar. Default `true`. */
45
- showMarkdownHint?: boolean;
46
- /** Text of the markdown hint. Default `"Markdown supported"`. */
47
- markdownHintLabel?: string;
48
- /** Placeholder shown in the Preview tab when there is nothing to render. */
49
- previewEmptyLabel?: string;
50
- /** Text shown in the Preview tab while the renderer is (lazy-)loading. */
51
- previewLoadingLabel?: string;
52
34
  /**
53
- * Override the Preview renderer. Receives the raw markdown, returns (or
54
- * resolves to) an HTML string that is rendered as-is **you are responsible
55
- * for sanitizing it**. When omitted, the bundled marked + DOMPurify pipeline
56
- * is used (lazy-loaded; requires the optional `marked` / `dompurify` peers).
35
+ * Formatting toolbar. `true` shows {@link DEFAULT_COMMENT_TOOLBAR}, `false`
36
+ * hides it, or pass an ordered array of {@link ToolbarItem}s (`"|"` for a
37
+ * separator). Default {@link DEFAULT_COMMENT_TOOLBAR}.
57
38
  */
58
- renderMarkdown?: MarkdownRenderer;
39
+ toolbar?: boolean | ToolbarItem[];
40
+ /** Toolbar used on mobile / touch devices. Same shape as `toolbar`. */
41
+ mobileToolbar?: boolean | ToolbarItem[];
42
+ /** Start in source mode on mobile / touch devices. Default `true`. */
43
+ autoSourceOnMobile?: boolean;
44
+ /** Media query defining "mobile". Default `"(pointer: coarse) and (max-width: 640px)"`. */
45
+ mobileQuery?: string;
46
+ /** URL prompt used by the link/image buttons. Defaults to `window.prompt`. */
47
+ prompt?: PromptFn;
48
+ /** Cap the editing surface height (long content scrolls inside). Default `32rem`. */
49
+ maxHeight?: number | string;
50
+ /** Also cap the surface to the parent's available height. Default `true`. */
51
+ capToParent?: boolean;
52
+ /** Show the WYSIWYG/Source mode toggle. Default `true`. */
53
+ showModeToggle?: boolean;
54
+ /** Toggle label while in WYSIWYG mode (switches to source). Default `"Source"`. */
55
+ sourceLabel?: string;
56
+ /** Toggle label while in source mode (switches to WYSIWYG). Default `"Preview"`. */
57
+ previewLabel?: string;
58
+ /** Wire ⌘/Ctrl+B / I / K to bold / italic / link. Default `true`. */
59
+ useShortcuts?: boolean;
59
60
  /**
60
61
  * Submit handler. Receives the current value. May be async — the submit
61
62
  * button shows a spinner and the box is disabled while it resolves.
@@ -73,7 +74,7 @@ export interface Props extends Omit<HTMLTextareaAttributes, "value" | "class" |
73
74
  showSubmit?: boolean;
74
75
  /** Show the cancel button. Defaults to `true` when `onCancel` is set. */
75
76
  showCancel?: boolean;
76
- /** Submit on ⌘/Ctrl+Enter while the textarea is focused. Default `true`. */
77
+ /** Submit on ⌘/Ctrl+Enter while the editor is focused. Default `true`. */
77
78
  submitOnModEnter?: boolean;
78
79
  /** Clear the value after a successful submit. Default `true`. */
79
80
  clearOnSubmit?: boolean;
@@ -90,17 +91,14 @@ export interface Props extends Omit<HTMLTextareaAttributes, "value" | "class" |
90
91
  avatar?: SnippetWithId | THC;
91
92
  /** Extra content rendered in the footer, left of the buttons (e.g. attach). */
92
93
  footer?: Snippet;
93
- /** Classes for the `<textarea>` element. */
94
+ /** Classes for the editor surface element (forwarded to MarkdownEditor). */
94
95
  classInput?: string;
95
- /** Classes for the tab bar. */
96
+ /** Classes for the toolbar bar (forwarded to MarkdownEditor's `classToolbar`). */
96
97
  classBar?: string;
97
- /** Classes for the preview panel. */
98
- classPreview?: string;
99
98
  /** Classes for the footer (button row). */
100
99
  classFooter?: string;
101
100
  style?: string;
102
101
  }
103
- import { type ValidationResult } from "../../actions/validate.svelte.js";
104
102
  declare const CommentInput: import("svelte").Component<Props, {
105
103
  validate: () => ValidationResult | undefined;
106
104
  clearValidation: () => void;
@@ -1,13 +1,16 @@
1
1
  # CommentInput
2
2
 
3
- A GitHub-style comment box: a **raw markdown** `<textarea>` with **Write / Preview**
4
- tabs and a submit footer. It composes the same `InputWrap` scaffold as the
5
- `Field*` components, so its label / description / validation / sizing / `class*`
6
- props and look-and-feel match the rest of the form family.
7
-
8
- Unlike `MarkdownEditor` (a WYSIWYG Milkdown/CodeMirror surface), `CommentInput` keeps
9
- the lightweight "type raw markdown, flip to a rendered preview" model — ideal for
10
- comments, replies and short discussion input.
3
+ A GitHub-style **comment composer** built on top of [`MarkdownEditor`](../MarkdownEditor/README.md).
4
+ It pairs a rich **WYSIWYG / source** markdown editor (a minimal formatting toolbar
5
+ plus a built-in **Source** toggle) with an optional avatar gutter and a
6
+ submit / cancel footer. Field chrome (label / description / validation / sizing /
7
+ theming) comes from the same `InputWrap` scaffold as the `Field*` family, so it
8
+ matches the rest of the form family.
9
+
10
+ Where `MarkdownEditor` is the full-featured editor, `CommentInput` is the
11
+ lightweight, opinionated wrapper for the common "leave a comment / reply" use
12
+ case: a clean minimal toolbar, an avatar, a submit button, and
13
+ ⌘/Ctrl+Enter-to-send — without you having to wire any of it up.
11
14
 
12
15
  ```svelte
13
16
  <script>
@@ -28,90 +31,114 @@ comments, replies and short discussion input.
28
31
  />
29
32
  ```
30
33
 
31
- ## Preview rendering & dependencies
34
+ ## Dependencies
32
35
 
33
- The Preview tab renders markdown to **sanitized** HTML. The bundled default
34
- renderer uses `marked` + `dompurify`, declared as **optional peer dependencies**
35
- and lazy-loaded the first time a Preview is opened:
36
+ Because the editing surface **is** `MarkdownEditor`, `CommentInput` inherits its
37
+ **optional peer dependencies** Milkdown (WYSIWYG) and CodeMirror (source) — which
38
+ are **lazy-loaded** the first time the editor mounts:
36
39
 
37
40
  ```sh
38
- npm i marked dompurify
41
+ npm i @milkdown/core @milkdown/ctx @milkdown/plugin-history \
42
+ @milkdown/plugin-listener @milkdown/preset-commonmark @milkdown/preset-gfm \
43
+ @milkdown/prose @milkdown/transformer @milkdown/utils \
44
+ @codemirror/commands @codemirror/lang-markdown @codemirror/language \
45
+ @codemirror/language-data @codemirror/state @codemirror/view
39
46
  ```
40
47
 
41
- If you don't install them, install your own renderer via `renderMarkdown` instead
42
- (the bundled one is then never imported):
48
+ This is the same peer set as `MarkdownEditor` see its README for the
49
+ authoritative list. If they aren't installed, the surface stays empty and a
50
+ console error explains why.
51
+
52
+ ## Toolbar
53
+
54
+ `toolbar` accepts `true` (the minimal default), `false` (hidden), or an ordered
55
+ array of items (`"|"` for a separator):
43
56
 
44
57
  ```svelte
45
- <CommentInput bind:value renderMarkdown={(md) => mySanitizedHtml(md)} />
58
+ <!-- Minimal default: ["bold", "italic", "link", "codeBlock"] -->
59
+ <CommentInput bind:value onSubmit={post} />
60
+
61
+ <!-- Custom set -->
62
+ <CommentInput
63
+ bind:value
64
+ toolbar={["bold", "italic", "|", "bulletList", "orderedList", "|", "link"]}
65
+ onSubmit={post}
66
+ />
67
+
68
+ <!-- No toolbar (still WYSIWYG, drives via shortcuts / the Source toggle) -->
69
+ <CommentInput bind:value toolbar={false} onSubmit={post} />
46
70
  ```
47
71
 
48
- > Comment text is untrusted user content and `marked` emits embedded raw HTML
49
- > verbatim. If you pass your own `renderMarkdown`, **you** are responsible for
50
- > sanitizing its output.
72
+ Available items: `bold`, `italic`, `heading1`–`heading6`, `link`, `image`,
73
+ `bulletList`, `orderedList`, `blockquote`, `codeBlock`, `hr`, `hardBreak`, `undo`,
74
+ `redo` (and `"|"`). The default is exported as `DEFAULT_COMMENT_TOOLBAR`.
75
+
76
+ ## Keyboard
77
+
78
+ | Shortcut | Action | Prop |
79
+ | ------------------ | -------------------- | ------------------ |
80
+ | ⌘/Ctrl + Enter | Submit | `submitOnModEnter` |
81
+ | ⌘/Ctrl + B / I / K | Bold / Italic / Link | `useShortcuts` |
51
82
 
52
83
  ## Props
53
84
 
54
- | Prop | Type | Default | Description |
55
- | ------------------------- | ------------------------------------------- | ------------------------ | ----------------------------------------------------------- |
56
- | `value` | `string` (bindable) | `""` | Comment text (raw markdown). |
57
- | `mode` | `"write" \| "preview"` (bindable) | `"write"` | Active tab. |
58
- | `input` | `HTMLTextAreaElement` (bindable) | — | The underlying textarea element. |
59
- | `name` | `string` | — | Textarea `name`, for native form submission. |
60
- | `label` | `THC \| Snippet` | `""` | Field label (via InputWrap). |
61
- | `description` | `THC \| Snippet` | — | Collapsible hint below the box. |
62
- | `placeholder` | `string` | — | Textarea placeholder. |
63
- | `renderSize` | `"sm" \| "md" \| "lg" \| string` | `"md"` | Size variant. |
64
- | `required` | `boolean` | `false` | Mark required + enforce on validation. |
65
- | `disabled` | `boolean` | `false` | Disable the whole box. |
66
- | `validate` | `boolean \| ValidateOptions` | — | Enable validation (same contract as `FieldTextarea`). |
67
- | `useTrim` | `boolean` | `true` | Trim surrounding whitespace on blur. |
68
- | `useAutogrow` | `boolean \| { enabled?, max? }` | `true` | Grow the textarea with content (default max 250px). |
69
- | `showTabs` | `boolean` | `true` | Show Write/Preview tabs. `false` plain markdown textarea. |
70
- | `writeLabel` | `string` | `"Write"` | Write tab label. |
71
- | `previewLabel` | `string` | `"Preview"` | Preview tab label. |
72
- | `showMarkdownHint` | `boolean` | `true` | Show the subtle "Markdown supported" hint. |
73
- | `markdownHintLabel` | `string` | `"Markdown supported"` | Hint text. |
74
- | `previewEmptyLabel` | `string` | `"Nothing to preview"` | Shown in Preview when empty. |
75
- | `previewLoadingLabel` | `string` | `"Loading preview…"` | Shown while the Preview renderer is loading. |
76
- | `renderMarkdown` | `(md: string) => string \| Promise<string>` | bundled marked+DOMPurify | Override the Preview renderer. Output is rendered as-is. |
77
- | `onSubmit` | `(value: string) => void \| Promise<void>` | — | Submit handler. Async spinner + disabled while pending. |
78
- | `onCancel` | `() => void` | — | Cancel handler. |
79
- | `onChange` | `(value: string) => void` | — | Fired on every edit. |
80
- | `submitLabel` | `string` | `"Comment"` | Submit button label. |
81
- | `cancelLabel` | `string` | `"Cancel"` | Cancel button label. |
82
- | `showSubmit` | `boolean` | `!!onSubmit` | Show the submit button. |
83
- | `showCancel` | `boolean` | `!!onCancel` | Show the cancel button. |
84
- | `submitOnModEnter` | `boolean` | `true` | Submit on ⌘/Ctrl+Enter. |
85
- | `clearOnSubmit` | `boolean` | `true` | Clear value after a successful submit. |
86
- | `submitDisabledWhenEmpty` | `boolean` | `true` | Disable submit when empty/whitespace. |
87
- | `busy` | `boolean` | `false` | External busy state (disables box + submit). |
88
- | `avatar` | `THC \| Snippet` | | Optional gutter to the left of the box. |
89
- | `footer` | `Snippet` | — | Extra footer content, left of the buttons. |
85
+ | Prop | Type | Default | Description |
86
+ | ------------------------- | ------------------------------------------ | ------------------------- | --------------------------------------------------------- |
87
+ | `value` | `string` (bindable) | `""` | Comment content (markdown). |
88
+ | `mode` | `"wysiwyg" \| "source"` (bindable) | `"source"` | Active editing surface (opens on raw markdown). |
89
+ | `input` | `HTMLDivElement` (bindable) | — | The editor surface wrapper element. |
90
+ | `name` | `string` | — | Hidden input `name`, for native form submission. |
91
+ | `label` | `THC \| Snippet` | `""` | Field label (via InputWrap). |
92
+ | `description` | `THC \| Snippet` | — | Collapsible hint below the box. |
93
+ | `placeholder` | `string` | — | Editor placeholder. |
94
+ | `renderSize` | `"sm" \| "md" \| "lg" \| string` | `"md"` | Size variant. |
95
+ | `required` | `boolean` | `false` | Mark required + enforce on validation. |
96
+ | `disabled` | `boolean` | `false` | Disable the whole box. |
97
+ | `validate` | `boolean \| ValidateOptions` | — | Enable validation (same contract as `MarkdownEditor`). |
98
+ | `toolbar` | `boolean \| ToolbarItem[]` | `DEFAULT_COMMENT_TOOLBAR` | Formatting toolbar config. |
99
+ | `mobileToolbar` | `boolean \| ToolbarItem[]` | MarkdownEditor default | Toolbar on touch devices. |
100
+ | `autoSourceOnMobile` | `boolean` | `true` | Start in source mode on mobile. |
101
+ | `mobileQuery` | `string` | `(pointer: coarse) …` | Media query defining "mobile". |
102
+ | `prompt` | `PromptFn` | `window.prompt` | URL prompt used by the link/image buttons. |
103
+ | `maxHeight` | `number \| string` | `32rem` | Cap the editing surface height (scrolls inside). |
104
+ | `capToParent` | `boolean` | `true` | Also cap the surface to the parent's available height. |
105
+ | `showModeToggle` | `boolean` | `true` | Show the WYSIWYG/Source toggle. |
106
+ | `sourceLabel` | `string` | `"Source"` | Toggle label while in WYSIWYG mode. |
107
+ | `previewLabel` | `string` | `"Preview"` | Toggle label while in source mode. |
108
+ | `useShortcuts` | `boolean` | `true` | Wire ⌘/Ctrl+B / I / K to bold / italic / link. |
109
+ | `onSubmit` | `(value: string) => void \| Promise<void>` | — | Submit handler. Async → spinner + disabled while pending. |
110
+ | `onCancel` | `() => void` | — | Cancel handler. |
111
+ | `onChange` | `(value: string) => void` | | Fired on every edit. |
112
+ | `submitLabel` | `string` | `"Comment"` | Submit button label. |
113
+ | `cancelLabel` | `string` | `"Cancel"` | Cancel button label. |
114
+ | `showSubmit` | `boolean` | `!!onSubmit` | Show the submit button. |
115
+ | `showCancel` | `boolean` | `!!onCancel` | Show the cancel button. |
116
+ | `submitOnModEnter` | `boolean` | `true` | Submit on ⌘/Ctrl+Enter. |
117
+ | `clearOnSubmit` | `boolean` | `true` | Clear value after a successful submit. |
118
+ | `submitDisabledWhenEmpty` | `boolean` | `true` | Disable submit when empty/whitespace. |
119
+ | `busy` | `boolean` | `false` | External busy state (disables box + submit). |
120
+ | `avatar` | `THC \| Snippet` | — | Optional gutter to the left of the box. |
121
+ | `footer` | `Snippet` | — | Extra footer content, left of the buttons. |
90
122
 
91
123
  Plus the standard `InputWrap` layout props (`labelAfter`, `below`, `labelLeft`,
92
- `labelLeftWidth`, `labelLeftBreakpoint`) and `class*` props (`classInput`,
93
- `classBar`, `classPreview`, `classFooter`, and the shared `InputWrapClassProps`).
124
+ `labelLeftWidth`, `labelLeftBreakpoint`) and `class*` props (`classInput` →
125
+ editor surface, `classBar` → toolbar, `classFooter` → button row, and the shared
126
+ `InputWrapClassProps`).
94
127
 
95
128
  ## Imperative API
96
129
 
97
130
  Bind the component (`bind:this`) to call: `validate()`, `clearValidation()`,
98
- `getValidation()`, `focus()`, `scrollIntoView()`, `submit()`.
99
-
100
- ## CSS variables
101
-
102
- All optional; each falls back to the shared `--stuic-input-*` token, then a global
103
- default.
104
-
105
- | Variable | Falls back to |
106
- | ------------------------------------- | ------------------------------ |
107
- | `--stuic-comment-input-radius` | `--stuic-input-radius` |
108
- | `--stuic-comment-input-border` | `--stuic-input-border` |
109
- | `--stuic-comment-input-border-focus` | `--stuic-input-border-focus` |
110
- | `--stuic-comment-input-bg` | `--stuic-input-bg` |
111
- | `--stuic-comment-input-padding-x` | `--stuic-input-padding-x-*` |
112
- | `--stuic-comment-input-padding-y` | `--stuic-input-padding-y-*` |
113
- | `--stuic-comment-input-font-size` | `--stuic-input-font-size-*` |
114
- | `--stuic-comment-input-min-height` | `5rem` (`4rem` sm / `7rem` lg) |
115
- | `--stuic-comment-input-gap` | `0.625rem` |
116
- | `--stuic-comment-input-tab-active-bg` | subtle overlay |
117
- | `--stuic-comment-input-code-bg` | subtle overlay |
131
+ `getValidation()`, `focus()`, `scrollIntoView()`, `submit()`. All delegate to the
132
+ embedded `MarkdownEditor`.
133
+
134
+ ## Theming
135
+
136
+ The editor card, toolbar and field chrome are themed via the
137
+ `--stuic-markdown-editor-*` and shared `--stuic-input-*` tokens (see the
138
+ `MarkdownEditor` README). `CommentInput` adds only its own layout tokens:
139
+
140
+ | Variable | Default | Description |
141
+ | ---------------------------------- | ---------- | ------------------------------------------ |
142
+ | `--stuic-comment-input-gap` | `0.625rem` | Gap between the avatar and the box. |
143
+ | `--stuic-comment-input-footer-gap` | `0.5rem` | Gap between the editor and the footer. |
144
+ | `--stuic-comment-input-min-height` | `5rem` | Editor min-height (`4rem` sm / `7rem` lg). |