kempo-ui 0.4.5 → 0.4.7
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/Chat.js +201 -0
- package/dist/components/CodeEditor.js +21 -2
- package/dist/components/Dropdown.js +1 -1
- package/dist/components/HtmlEditor.js +42 -3
- package/dist/components/Markdown.js +1 -0
- package/dist/components/MarkdownEditor.js +173 -0
- package/dist/components/Resize.js +12 -2
- package/dist/components/markdownEditorControls/Bold.js +11 -0
- package/dist/components/markdownEditorControls/BulletList.js +11 -0
- package/dist/components/markdownEditorControls/Code.js +11 -0
- package/dist/components/markdownEditorControls/FormatBlock.js +30 -0
- package/dist/components/markdownEditorControls/Heading.js +11 -0
- package/dist/components/markdownEditorControls/Image.js +93 -0
- package/dist/components/markdownEditorControls/Italic.js +11 -0
- package/dist/components/markdownEditorControls/Link.js +11 -0
- package/dist/components/markdownEditorControls/MarkdownEditorControl.js +39 -0
- package/dist/components/markdownEditorControls/Menu.js +30 -0
- package/dist/components/markdownEditorControls/NumberedList.js +11 -0
- package/dist/components/markdownEditorControls/Quote.js +11 -0
- package/dist/components/markdownEditorControls/SpeechToText.js +16 -0
- package/dist/components/markdownEditorControls/Strikethrough.js +11 -0
- package/dist/components/markdownEditorControls/Table.js +80 -0
- package/dist/utils/marked.esm.js +1 -0
- package/dist/utils/renderMarkdown.js +1 -0
- package/dist/utils/sanitizeHtml.js +1 -0
- package/docs/components/accordion.html +16 -0
- package/docs/components/aside.html +16 -0
- package/docs/components/calendar.html +16 -0
- package/docs/components/card.html +16 -0
- package/docs/components/chat.html +719 -0
- package/docs/components/code-editor.html +52 -0
- package/docs/components/color-picker.html +16 -0
- package/docs/components/combobox.html +16 -0
- package/docs/components/content-slider.html +16 -0
- package/docs/components/context.html +16 -0
- package/docs/components/dialog.html +16 -0
- package/docs/components/dropdown.html +16 -0
- package/docs/components/filter-list.html +16 -0
- package/docs/components/focus-capture.html +16 -0
- package/docs/components/html-editor.html +57 -0
- package/docs/components/hybrid-component.html +16 -0
- package/docs/components/icon.html +16 -0
- package/docs/components/import.html +16 -0
- package/docs/components/light-component.html +16 -0
- package/docs/components/markdown-editor.html +928 -0
- package/docs/components/markdown.html +650 -0
- package/docs/components/nav-spacer.html +16 -0
- package/docs/components/nav.html +16 -0
- package/docs/components/photo-viewer.html +16 -0
- package/docs/components/progress.html +16 -0
- package/docs/components/resize.html +19 -0
- package/docs/components/rich-textarea.html +650 -0
- package/docs/components/shadow-component.html +16 -0
- package/docs/components/show-more.html +16 -0
- package/docs/components/slider.html +16 -0
- package/docs/components/sortable.html +16 -0
- package/docs/components/speech-to-text.html +16 -0
- package/docs/components/spinner.html +16 -0
- package/docs/components/split.html +16 -0
- package/docs/components/table.html +16 -0
- package/docs/components/tableControls.html +16 -0
- package/docs/components/tableCustomFields.html +16 -0
- package/docs/components/tableFetchRecords.html +16 -0
- package/docs/components/tableFieldSortHide.html +16 -0
- package/docs/components/tablePagination.html +16 -0
- package/docs/components/tablePlaceholder.html +16 -0
- package/docs/components/tableRecordEditing.html +16 -0
- package/docs/components/tableRecordFiltering.html +16 -0
- package/docs/components/tableRecordHiding.html +16 -0
- package/docs/components/tableRecordSearching.html +16 -0
- package/docs/components/tableRecordSelection.html +16 -0
- package/docs/components/tableRowControls.html +16 -0
- package/docs/components/tableServerSync.html +16 -0
- package/docs/components/tableSorting.html +16 -0
- package/docs/components/tabs.html +16 -0
- package/docs/components/tags.html +16 -0
- package/docs/components/text-to-speech.html +16 -0
- package/docs/components/theme-select.html +16 -0
- package/docs/components/theme-switcher.html +16 -0
- package/docs/components/time.html +16 -0
- package/docs/components/timestamp.html +16 -0
- package/docs/components/toast.html +16 -0
- package/docs/components/toggle.html +16 -0
- package/docs/components/tree.html +16 -0
- package/docs/components/voice-selector.html +16 -0
- package/docs/icons/done_all.svg +1 -0
- package/docs/icons/format_quote.svg +1 -0
- package/docs/icons/format_strikethrough.svg +1 -0
- package/docs/icons/send.svg +1 -0
- package/docs/icons/text_fields.svg +1 -0
- package/docs/index.html +40 -0
- package/docs/src/components/Chat.js +201 -0
- package/docs/src/components/CodeEditor.js +21 -2
- package/docs/src/components/Dropdown.js +1 -1
- package/docs/src/components/HtmlEditor.js +42 -3
- package/docs/src/components/Markdown.js +1 -0
- package/docs/src/components/MarkdownEditor.js +173 -0
- package/docs/src/components/Resize.js +12 -2
- package/docs/src/components/markdownEditorControls/Bold.js +11 -0
- package/docs/src/components/markdownEditorControls/BulletList.js +11 -0
- package/docs/src/components/markdownEditorControls/Code.js +11 -0
- package/docs/src/components/markdownEditorControls/FormatBlock.js +30 -0
- package/docs/src/components/markdownEditorControls/Heading.js +11 -0
- package/docs/src/components/markdownEditorControls/Image.js +93 -0
- package/docs/src/components/markdownEditorControls/Italic.js +11 -0
- package/docs/src/components/markdownEditorControls/Link.js +11 -0
- package/docs/src/components/markdownEditorControls/MarkdownEditorControl.js +39 -0
- package/docs/src/components/markdownEditorControls/Menu.js +30 -0
- package/docs/src/components/markdownEditorControls/NumberedList.js +11 -0
- package/docs/src/components/markdownEditorControls/Quote.js +11 -0
- package/docs/src/components/markdownEditorControls/SpeechToText.js +16 -0
- package/docs/src/components/markdownEditorControls/Strikethrough.js +11 -0
- package/docs/src/components/markdownEditorControls/Table.js +80 -0
- package/docs/src/utils/marked.esm.js +1 -0
- package/docs/src/utils/renderMarkdown.js +1 -0
- package/docs/src/utils/sanitizeHtml.js +1 -0
- package/docs/utils/context.html +16 -0
- package/docs/utils/cookie.html +16 -0
- package/docs/utils/debounce.html +16 -0
- package/docs/utils/drag.html +16 -0
- package/docs/utils/elevation.html +20 -10
- package/docs/utils/formatTimestamp.html +16 -0
- package/docs/utils/object.html +16 -0
- package/docs/utils/propConverters.html +16 -0
- package/docs/utils/sanitizeHtml.html +559 -0
- package/docs/utils/string.html +16 -0
- package/docs/utils/theme.html +16 -0
- package/docs/utils/toTitleCase.html +16 -0
- package/docs/utils/type.html +16 -0
- package/docs/utils/voice.html +16 -0
- package/docs/utils/wait.html +16 -0
- package/docs-src/components/chat.page.html +230 -0
- package/docs-src/components/code-editor.page.html +36 -0
- package/docs-src/components/html-editor.page.html +41 -0
- package/docs-src/components/markdown-editor.page.html +439 -0
- package/docs-src/components/markdown.page.html +161 -0
- package/docs-src/components/resize.page.html +3 -0
- package/docs-src/index.page.html +24 -0
- package/docs-src/nav.fragment.html +16 -0
- package/docs-src/utils/elevation.page.html +4 -10
- package/docs-src/utils/sanitizeHtml.page.html +73 -0
- package/icons/done_all.svg +1 -0
- package/icons/format_quote.svg +1 -0
- package/icons/format_strikethrough.svg +1 -0
- package/icons/send.svg +1 -0
- package/icons/text_fields.svg +1 -0
- package/llms.txt +6 -2
- package/package.json +3 -2
- package/src/components/Chat.js +435 -0
- package/src/components/CodeEditor.js +58 -2
- package/src/components/Dropdown.js +1 -1
- package/src/components/HtmlEditor.js +84 -4
- package/src/components/Markdown.js +125 -0
- package/src/components/MarkdownEditor.js +654 -0
- package/src/components/Resize.js +17 -1
- package/src/components/markdownEditorControls/Bold.js +30 -0
- package/src/components/markdownEditorControls/BulletList.js +30 -0
- package/src/components/markdownEditorControls/Code.js +39 -0
- package/src/components/markdownEditorControls/FormatBlock.js +102 -0
- package/src/components/markdownEditorControls/Heading.js +45 -0
- package/src/components/markdownEditorControls/Image.js +250 -0
- package/src/components/markdownEditorControls/Italic.js +30 -0
- package/src/components/markdownEditorControls/Link.js +74 -0
- package/src/components/markdownEditorControls/MarkdownEditorControl.js +158 -0
- package/src/components/markdownEditorControls/Menu.js +86 -0
- package/src/components/markdownEditorControls/NumberedList.js +69 -0
- package/src/components/markdownEditorControls/Quote.js +30 -0
- package/src/components/markdownEditorControls/SpeechToText.js +65 -0
- package/src/components/markdownEditorControls/Strikethrough.js +35 -0
- package/src/components/markdownEditorControls/Table.js +164 -0
- package/src/utils/marked.esm.js +77 -0
- package/src/utils/renderMarkdown.js +29 -0
- package/src/utils/sanitizeHtml.js +137 -0
- package/tests/components/Chat.browser-test.js +540 -0
- package/tests/components/Markdown.browser-test.js +292 -0
- package/tests/utils/sanitizeHtml.browser-test.js +179 -0
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Chat - Components - Kempo Docs - A Web Components Solution</title>
|
|
7
|
+
<link rel="icon" type="image/png" sizes="48x48" href="../media/icon48.png">
|
|
8
|
+
<link rel="manifest" href="../manifest.json" />
|
|
9
|
+
<link rel="stylesheet" href="../kempo-vars.css" />
|
|
10
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/kempo-css@2/dist/kempo.min.css" />
|
|
11
|
+
<link rel="stylesheet" href="../kempo-hljs.css" />
|
|
12
|
+
<link rel="stylesheet" href="../styles.css" />
|
|
13
|
+
<script>window.litDisableBundleWarning = true;</script>
|
|
14
|
+
|
|
15
|
+
</head>
|
|
16
|
+
<body>
|
|
17
|
+
|
|
18
|
+
<k-nav
|
|
19
|
+
fixed
|
|
20
|
+
class="bg-primary"
|
|
21
|
+
>
|
|
22
|
+
<button
|
|
23
|
+
id="toggleNavSideMenu"
|
|
24
|
+
class="link"
|
|
25
|
+
>
|
|
26
|
+
<k-icon name="menu"></k-icon>
|
|
27
|
+
</button>
|
|
28
|
+
<a
|
|
29
|
+
href="../"
|
|
30
|
+
class="d-if ph"
|
|
31
|
+
style="align-items: center"
|
|
32
|
+
>
|
|
33
|
+
<img
|
|
34
|
+
src="../media/icon32.png"
|
|
35
|
+
alt="Kempo UI Icon"
|
|
36
|
+
class="pr"
|
|
37
|
+
/>
|
|
38
|
+
Kempo UI
|
|
39
|
+
</a>
|
|
40
|
+
<div
|
|
41
|
+
style="position:relative;margin:0 .25rem;align-self:center;"
|
|
42
|
+
theme="light"
|
|
43
|
+
>
|
|
44
|
+
<input
|
|
45
|
+
id="navSearchInput"
|
|
46
|
+
type="search"
|
|
47
|
+
placeholder="Search docs..."
|
|
48
|
+
autocomplete="off"
|
|
49
|
+
style="width:11rem;"
|
|
50
|
+
/>
|
|
51
|
+
<div
|
|
52
|
+
id="navSearchDropdown"
|
|
53
|
+
hidden
|
|
54
|
+
theme="light"
|
|
55
|
+
style="position:absolute;top:calc(100% + 4px);left:0;width:min(480px,90vw);max-height:70vh;background:var(--c_bg);border:1px solid var(--c_border);border-radius:var(--radius);overflow:auto;z-index:9999;box-shadow:0 4px 12px rgba(0,0,0,.3);"
|
|
56
|
+
>
|
|
57
|
+
<k-filter-list
|
|
58
|
+
id="navSearchList"
|
|
59
|
+
style="display:block;padding:.25rem 0;"
|
|
60
|
+
>
|
|
61
|
+
<k-filter-item filter-keywords="accordion components"><a
|
|
62
|
+
href="../components/accordion.html">Accordion<br><small>Component</small></a></k-filter-item>
|
|
63
|
+
<k-filter-item filter-keywords="aside sidebar components"><a
|
|
64
|
+
href="../components/aside.html">Aside<br><small>Component</small></a></k-filter-item>
|
|
65
|
+
<k-filter-item filter-keywords="calendar date picker range month day components"><a
|
|
66
|
+
href="../components/calendar.html"
|
|
67
|
+
>Calendar<br><small>Component</small></a></k-filter-item>
|
|
68
|
+
<k-filter-item filter-keywords="card components"><a
|
|
69
|
+
href="../components/card.html">Card<br><small>Component</small></a></k-filter-item>
|
|
70
|
+
<k-filter-item filter-keywords="chat message conversation messaging components"><a
|
|
71
|
+
href="../components/chat.html"
|
|
72
|
+
>Chat<br><small>Component</small></a></k-filter-item>
|
|
73
|
+
<k-filter-item filter-keywords="code editor codeeditor monaco components"><a
|
|
74
|
+
href="../components/code-editor.html"
|
|
75
|
+
>Code Editor<br><small>Component</small></a></k-filter-item>
|
|
76
|
+
<k-filter-item filter-keywords="combobox combo box search autocomplete components"><a
|
|
77
|
+
href="../components/combobox.html"
|
|
78
|
+
>Combobox<br><small>Component</small></a></k-filter-item>
|
|
79
|
+
<k-filter-item filter-keywords="color picker colorpicker components"><a
|
|
80
|
+
href="../components/color-picker.html"
|
|
81
|
+
>Color Picker<br><small>Component</small></a></k-filter-item>
|
|
82
|
+
<k-filter-item filter-keywords="content slider components"><a
|
|
83
|
+
href="../components/content-slider.html"
|
|
84
|
+
>Content Slider<br><small>Component</small></a></k-filter-item>
|
|
85
|
+
<k-filter-item filter-keywords="context state global components"><a
|
|
86
|
+
href="../components/context.html"
|
|
87
|
+
>Context<br><small>Component</small></a></k-filter-item>
|
|
88
|
+
<k-filter-item filter-keywords="dialog modal popup components"><a
|
|
89
|
+
href="../components/dialog.html">Dialog<br><small>Component</small></a></k-filter-item>
|
|
90
|
+
<k-filter-item filter-keywords="dropdown select components"><a
|
|
91
|
+
href="../components/dropdown.html">Dropdown<br><small>Component</small></a></k-filter-item>
|
|
92
|
+
<k-filter-item filter-keywords="filter list filterlist components"><a
|
|
93
|
+
href="../components/filter-list.html"
|
|
94
|
+
>Filter List<br><small>Component</small></a></k-filter-item>
|
|
95
|
+
<k-filter-item filter-keywords="focus capture focuscapture components"><a
|
|
96
|
+
href="../components/focus-capture.html"
|
|
97
|
+
>Focus Capture<br><small>Component</small></a></k-filter-item>
|
|
98
|
+
<k-filter-item filter-keywords="html editor components"><a
|
|
99
|
+
href="../components/html-editor.html">HTML
|
|
100
|
+
Editor<br><small>Component</small></a></k-filter-item>
|
|
101
|
+
<k-filter-item filter-keywords="markdown render parser marked gfm components"><a
|
|
102
|
+
href="../components/markdown.html"
|
|
103
|
+
>Markdown<br><small>Component</small></a></k-filter-item>
|
|
104
|
+
<k-filter-item filter-keywords="markdown editor markdowneditor write preview marked gfm tables components"><a
|
|
105
|
+
href="../components/markdown-editor.html"
|
|
106
|
+
>Markdown Editor<br><small>Component</small></a></k-filter-item>
|
|
107
|
+
<k-filter-item filter-keywords="icon components"><a
|
|
108
|
+
href="../components/icon.html">Icon<br><small>Component</small></a></k-filter-item>
|
|
109
|
+
<k-filter-item filter-keywords="import components"><a
|
|
110
|
+
href="../components/import.html">Import<br><small>Component</small></a></k-filter-item>
|
|
111
|
+
<k-filter-item filter-keywords="nav navbar navigation components"><a
|
|
112
|
+
href="../components/nav.html">Nav<br><small>Component</small></a></k-filter-item>
|
|
113
|
+
<k-filter-item filter-keywords="nav spacer navbar spacer navspacer components"><a
|
|
114
|
+
href="../components/nav-spacer.html"
|
|
115
|
+
>Nav Spacer<br><small>Component</small></a></k-filter-item>
|
|
116
|
+
<k-filter-item filter-keywords="photo viewer photoviewer components"><a
|
|
117
|
+
href="../components/photo-viewer.html"
|
|
118
|
+
>Photo Viewer<br><small>Component</small></a></k-filter-item>
|
|
119
|
+
<k-filter-item filter-keywords="progress loading components"><a
|
|
120
|
+
href="../components/progress.html">Progress<br><small>Component</small></a></k-filter-item>
|
|
121
|
+
<k-filter-item filter-keywords="resize components"><a
|
|
122
|
+
href="../components/resize.html">Resize<br><small>Component</small></a></k-filter-item>
|
|
123
|
+
<k-filter-item filter-keywords="show more showmore components"><a
|
|
124
|
+
href="../components/show-more.html"
|
|
125
|
+
>Show More<br><small>Component</small></a></k-filter-item>
|
|
126
|
+
<k-filter-item filter-keywords="slider range input step components"><a
|
|
127
|
+
href="../components/slider.html"
|
|
128
|
+
>Slider<br><small>Component</small></a></k-filter-item>
|
|
129
|
+
<k-filter-item filter-keywords="sortable drag drop sort components"><a
|
|
130
|
+
href="../components/sortable.html"
|
|
131
|
+
>Sortable<br><small>Component</small></a></k-filter-item>
|
|
132
|
+
<k-filter-item filter-keywords="speech to text speechtotext microphone voice recognition transcription components"><a
|
|
133
|
+
href="../components/speech-to-text.html"
|
|
134
|
+
>Speech To Text<br><small>Component</small></a></k-filter-item>
|
|
135
|
+
<k-filter-item filter-keywords="spinner loading components"><a
|
|
136
|
+
href="../components/spinner.html">Spinner<br><small>Component</small></a></k-filter-item>
|
|
137
|
+
<k-filter-item filter-keywords="split pane components"><a
|
|
138
|
+
href="../components/split.html">Split<br><small>Component</small></a></k-filter-item>
|
|
139
|
+
<k-filter-item filter-keywords="table data grid components"><a
|
|
140
|
+
href="../components/table.html">Table<br><small>Component</small></a></k-filter-item>
|
|
141
|
+
<k-filter-item filter-keywords="table controls advanced example"><a
|
|
142
|
+
href="../components/tableControls.html"
|
|
143
|
+
>Table — Controls<br><small>Advanced Example</small></a></k-filter-item>
|
|
144
|
+
<k-filter-item filter-keywords="table custom fields advanced example"><a
|
|
145
|
+
href="../components/tableCustomFields.html"
|
|
146
|
+
>Table — Custom Fields<br><small>Advanced Example</small></a></k-filter-item>
|
|
147
|
+
<k-filter-item filter-keywords="table fetch records advanced example"><a
|
|
148
|
+
href="../components/tableFetchRecords.html"
|
|
149
|
+
>Table — Fetch Records<br><small>Advanced Example</small></a></k-filter-item>
|
|
150
|
+
<k-filter-item filter-keywords="table field sort hide advanced example"><a
|
|
151
|
+
href="../components/tableFieldSortHide.html"
|
|
152
|
+
>Table — Field Sort & Hide<br><small>Advanced Example</small></a></k-filter-item>
|
|
153
|
+
<k-filter-item filter-keywords="table pagination advanced example"><a
|
|
154
|
+
href="../components/tablePagination.html"
|
|
155
|
+
>Table — Pagination<br><small>Advanced Example</small></a></k-filter-item>
|
|
156
|
+
<k-filter-item filter-keywords="table record editing advanced example"><a
|
|
157
|
+
href="../components/tableRecordEditing.html"
|
|
158
|
+
>Table — Record Editing<br><small>Advanced Example</small></a></k-filter-item>
|
|
159
|
+
<k-filter-item filter-keywords="table record filtering advanced example"><a
|
|
160
|
+
href="../components/tableRecordFiltering.html"
|
|
161
|
+
>Table — Record Filtering<br><small>Advanced Example</small></a></k-filter-item>
|
|
162
|
+
<k-filter-item filter-keywords="table record hiding advanced example"><a
|
|
163
|
+
href="../components/tableRecordHiding.html"
|
|
164
|
+
>Table — Record Hiding<br><small>Advanced Example</small></a></k-filter-item>
|
|
165
|
+
<k-filter-item filter-keywords="table record searching advanced example"><a
|
|
166
|
+
href="../components/tableRecordSearching.html"
|
|
167
|
+
>Table — Record Searching<br><small>Advanced Example</small></a></k-filter-item>
|
|
168
|
+
<k-filter-item filter-keywords="table record selection advanced example"><a
|
|
169
|
+
href="../components/tableRecordSelection.html"
|
|
170
|
+
>Table — Record Selection<br><small>Advanced Example</small></a></k-filter-item>
|
|
171
|
+
<k-filter-item filter-keywords="table row controls advanced example"><a
|
|
172
|
+
href="../components/tableRowControls.html"
|
|
173
|
+
>Table — Row Controls<br><small>Advanced Example</small></a></k-filter-item>
|
|
174
|
+
<k-filter-item filter-keywords="table server sync advanced example"><a
|
|
175
|
+
href="../components/tableServerSync.html"
|
|
176
|
+
>Table — Server Sync<br><small>Advanced Example</small></a></k-filter-item>
|
|
177
|
+
<k-filter-item filter-keywords="table sorting advanced example"><a
|
|
178
|
+
href="../components/tableSorting.html"
|
|
179
|
+
>Table — Sorting<br><small>Advanced Example</small></a></k-filter-item>
|
|
180
|
+
<k-filter-item filter-keywords="table placeholder advanced example"><a
|
|
181
|
+
href="../components/tablePlaceholder.html"
|
|
182
|
+
>Table — Placeholder<br><small>Advanced Example</small></a></k-filter-item>
|
|
183
|
+
<k-filter-item filter-keywords="tabs tab panel components"><a
|
|
184
|
+
href="../components/tabs.html">Tabs<br><small>Component</small></a></k-filter-item>
|
|
185
|
+
<k-filter-item filter-keywords="tags tag input components"><a
|
|
186
|
+
href="../components/tags.html">Tags<br><small>Component</small></a></k-filter-item>
|
|
187
|
+
<k-filter-item filter-keywords="text to speech texttospeech tts speak voice synthesis components"><a
|
|
188
|
+
href="../components/text-to-speech.html"
|
|
189
|
+
>Text To Speech<br><small>Component</small></a></k-filter-item>
|
|
190
|
+
<k-filter-item filter-keywords="theme select dropdown dark light components"><a
|
|
191
|
+
href="../components/theme-select.html"
|
|
192
|
+
>Theme Select<br><small>Component</small></a></k-filter-item>
|
|
193
|
+
<k-filter-item filter-keywords="theme switcher dark light components"><a
|
|
194
|
+
href="../components/theme-switcher.html"
|
|
195
|
+
>Theme Switcher<br><small>Component</small></a></k-filter-item>
|
|
196
|
+
<k-filter-item filter-keywords="time picker clock hour minute form components"><a
|
|
197
|
+
href="../components/time.html"
|
|
198
|
+
>Time<br><small>Component</small></a></k-filter-item>
|
|
199
|
+
<k-filter-item filter-keywords="timestamp date time components"><a
|
|
200
|
+
href="../components/timestamp.html"
|
|
201
|
+
>Timestamp<br><small>Component</small></a></k-filter-item>
|
|
202
|
+
<k-filter-item filter-keywords="toast notification alert components"><a
|
|
203
|
+
href="../components/toast.html"
|
|
204
|
+
>Toast<br><small>Component</small></a></k-filter-item>
|
|
205
|
+
<k-filter-item filter-keywords="toggle switch checkbox components"><a
|
|
206
|
+
href="../components/toggle.html"
|
|
207
|
+
>Toggle<br><small>Component</small></a></k-filter-item>
|
|
208
|
+
<k-filter-item filter-keywords="tree treeview components"><a
|
|
209
|
+
href="../components/tree.html">Tree<br><small>Component</small></a></k-filter-item>
|
|
210
|
+
<k-filter-item filter-keywords="voice selector dropdown speech synthesis preference components"><a
|
|
211
|
+
href="../components/voice-selector.html"
|
|
212
|
+
>Voice Selector<br><small>Component</small></a></k-filter-item>
|
|
213
|
+
<k-filter-item filter-keywords="shadow component base"><a
|
|
214
|
+
href="../components/shadow-component.html"
|
|
215
|
+
>Shadow Component<br><small>Base Component</small></a></k-filter-item>
|
|
216
|
+
<k-filter-item filter-keywords="light component base"><a
|
|
217
|
+
href="../components/light-component.html">Light Component<br><small>Base
|
|
218
|
+
Component</small></a></k-filter-item>
|
|
219
|
+
<k-filter-item filter-keywords="hybrid component base"><a
|
|
220
|
+
href="../components/hybrid-component.html"
|
|
221
|
+
>Hybrid Component<br><small>Base Component</small></a></k-filter-item>
|
|
222
|
+
<k-filter-item filter-keywords="cookie utils utility"><a
|
|
223
|
+
href="../utils/cookie.html">cookie<br><small>Utility</small></a></k-filter-item>
|
|
224
|
+
<k-filter-item filter-keywords="context utils utility"><a
|
|
225
|
+
href="../utils/context.html">context<br><small>Utility</small></a></k-filter-item>
|
|
226
|
+
<k-filter-item filter-keywords="debounce utils utility"><a
|
|
227
|
+
href="../utils/debounce.html">debounce<br><small>Utility</small></a></k-filter-item>
|
|
228
|
+
<k-filter-item filter-keywords="drag utils utility"><a
|
|
229
|
+
href="../utils/drag.html">drag<br><small>Utility</small></a></k-filter-item>
|
|
230
|
+
<k-filter-item filter-keywords="elevation z-index stacking utils"><a
|
|
231
|
+
href="../utils/elevation.html">elevation<br><small>Utility</small></a></k-filter-item>
|
|
232
|
+
<k-filter-item filter-keywords="formattimestamp timestamp format date time utils utility"><a
|
|
233
|
+
href="../utils/formatTimestamp.html"
|
|
234
|
+
>formatTimestamp<br><small>Utility</small></a></k-filter-item>
|
|
235
|
+
<k-filter-item filter-keywords="object utils utility"><a
|
|
236
|
+
href="../utils/object.html">object<br><small>Utility</small></a></k-filter-item>
|
|
237
|
+
<k-filter-item filter-keywords="propconverters prop converters utils utility"><a
|
|
238
|
+
href="../utils/propConverters.html"
|
|
239
|
+
>propConverters<br><small>Utility</small></a></k-filter-item>
|
|
240
|
+
<k-filter-item filter-keywords="sanitizehtml sanitize html xss security utils utility"><a
|
|
241
|
+
href="../utils/sanitizeHtml.html"
|
|
242
|
+
>sanitizeHtml<br><small>Utility</small></a></k-filter-item>
|
|
243
|
+
<k-filter-item filter-keywords="string utils utility"><a
|
|
244
|
+
href="../utils/string.html">string<br><small>Utility</small></a></k-filter-item>
|
|
245
|
+
<k-filter-item filter-keywords="theme utils utility"><a
|
|
246
|
+
href="../utils/theme.html">theme<br><small>Utility</small></a></k-filter-item>
|
|
247
|
+
<k-filter-item filter-keywords="type utils utility"><a
|
|
248
|
+
href="../utils/type.html">type<br><small>Utility</small></a></k-filter-item>
|
|
249
|
+
<k-filter-item filter-keywords="voice speech synthesis preference utils utility"><a
|
|
250
|
+
href="../utils/voice.html">voice<br><small>Utility</small></a></k-filter-item>
|
|
251
|
+
<k-filter-item filter-keywords="wait async utils utility"><a
|
|
252
|
+
href="../utils/wait.html">wait<br><small>Utility</small></a></k-filter-item>
|
|
253
|
+
</k-filter-list>
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
<div class="flex"></div>
|
|
257
|
+
<a
|
|
258
|
+
href="https://github.com/dustinpoissant/kempo-ui?tab=License-1-ov-file#creative-commons-attribution-noncommercial-sharealike-20"
|
|
259
|
+
target="_blank"
|
|
260
|
+
><k-icon name="license"></k-icon></a>
|
|
261
|
+
<a
|
|
262
|
+
href="https://www.npmjs.com/package/kempo-ui"
|
|
263
|
+
target="_blank"
|
|
264
|
+
><k-icon name="npm"></k-icon></a>
|
|
265
|
+
<a
|
|
266
|
+
href="https://github.com/dustinpoissant/kempo-ui"
|
|
267
|
+
target="_blank"
|
|
268
|
+
><k-icon name="github-mark"></k-icon></a>
|
|
269
|
+
<k-theme-switcher
|
|
270
|
+
class="mr"
|
|
271
|
+
style="
|
|
272
|
+
--padding: 0.5rem;
|
|
273
|
+
--c_active: var(--tc_on_primary);
|
|
274
|
+
--tc_active: var(--c_primary);
|
|
275
|
+
--c_inactive__hover: rgba(255, 255, 255, 0.1);
|
|
276
|
+
"
|
|
277
|
+
></k-theme-switcher>
|
|
278
|
+
</k-nav>
|
|
279
|
+
<div style="width: 100%; height: 4rem;"></div>
|
|
280
|
+
<k-aside
|
|
281
|
+
id="navSideMenu"
|
|
282
|
+
main="overlay"
|
|
283
|
+
state="offscreen"
|
|
284
|
+
>
|
|
285
|
+
<menu>
|
|
286
|
+
<a
|
|
287
|
+
href="../"
|
|
288
|
+
class="ta-center bb mb r0"
|
|
289
|
+
>
|
|
290
|
+
<h1 class="tc-primary">Kempo UI</h1>
|
|
291
|
+
<img
|
|
292
|
+
src="../media/icon128.png"
|
|
293
|
+
alt="Kempo UI Icon"
|
|
294
|
+
/>
|
|
295
|
+
</a>
|
|
296
|
+
|
|
297
|
+
<div class="pl mb">
|
|
298
|
+
<h3>Components</h3>
|
|
299
|
+
<a href="../components/accordion.html">Accordion</a>
|
|
300
|
+
<a href="../components/aside.html">Aside</a>
|
|
301
|
+
<a href="../components/calendar.html">Calendar</a>
|
|
302
|
+
<a href="../components/card.html">Card</a>
|
|
303
|
+
<a href="../components/chat.html">Chat</a>
|
|
304
|
+
<a href="../components/code-editor.html">Code Editor</a>
|
|
305
|
+
<a href="../components/combobox.html">Combobox</a>
|
|
306
|
+
<a href="../components/color-picker.html">ColorPicker</a>
|
|
307
|
+
<a href="../components/content-slider.html">Content Slider</a>
|
|
308
|
+
<a href="../components/context.html">Context</a>
|
|
309
|
+
<a href="../components/dialog.html">Dialog</a>
|
|
310
|
+
<a href="../components/dropdown.html">Dropdown</a>
|
|
311
|
+
<a href="../components/filter-list.html">Filter List</a>
|
|
312
|
+
<a href="../components/focus-capture.html">FocusCapture</a>
|
|
313
|
+
<a href="../components/html-editor.html">HTML Editor</a>
|
|
314
|
+
<a href="../components/markdown.html">Markdown</a>
|
|
315
|
+
<a href="../components/markdown-editor.html">Markdown Editor</a>
|
|
316
|
+
<a href="../components/icon.html">Icon</a>
|
|
317
|
+
<a href="../components/import.html">Import</a>
|
|
318
|
+
<a href="../components/nav.html">Nav</a>
|
|
319
|
+
<a href="../components/nav-spacer.html">Nav Spacer</a>
|
|
320
|
+
<a href="../components/photo-viewer.html">Photo Viewer</a>
|
|
321
|
+
<a href="../components/progress.html">Progress</a>
|
|
322
|
+
<a href="../components/resize.html">Resize</a>
|
|
323
|
+
<a href="../components/show-more.html">Show More</a>
|
|
324
|
+
<a href="../components/slider.html">Slider</a>
|
|
325
|
+
<a href="../components/sortable.html">Sortable</a>
|
|
326
|
+
<a href="../components/speech-to-text.html">Speech To Text</a>
|
|
327
|
+
<a href="../components/spinner.html">Spinner</a>
|
|
328
|
+
<a href="../components/split.html">Split</a>
|
|
329
|
+
<a href="../components/table.html">Table</a>
|
|
330
|
+
<a href="../components/tabs.html">Tabs</a>
|
|
331
|
+
<a href="../components/tags.html">Tags</a>
|
|
332
|
+
<a href="../components/text-to-speech.html">Text To Speech</a>
|
|
333
|
+
<a href="../components/theme-select.html">Theme Select</a>
|
|
334
|
+
<a href="../components/theme-switcher.html">Theme Switcher</a>
|
|
335
|
+
<a href="../components/time.html">Time</a>
|
|
336
|
+
<a href="../components/timestamp.html">Timestamp</a>
|
|
337
|
+
<a href="../components/toast.html">Toast</a>
|
|
338
|
+
<a href="../components/toggle.html">Toggle</a>
|
|
339
|
+
<a href="../components/tree.html">Tree</a>
|
|
340
|
+
<a href="../components/voice-selector.html">Voice Selector</a>
|
|
341
|
+
</div>
|
|
342
|
+
|
|
343
|
+
<h5 class="mb0">Base Components</h5>
|
|
344
|
+
<div class="pl mb">
|
|
345
|
+
<a href="../components/shadow-component.html">Shadow Component</a>
|
|
346
|
+
<a href="../components/light-component.html">Light Component</a>
|
|
347
|
+
<a href="../components/hybrid-component.html">Hybrid Component</a>
|
|
348
|
+
</div>
|
|
349
|
+
|
|
350
|
+
<h5 class="mb0">Utils</h5>
|
|
351
|
+
<div class="pl mb">
|
|
352
|
+
<a href="../utils/cookie.html">cookie</a>
|
|
353
|
+
<a href="../utils/context.html">context</a>
|
|
354
|
+
<a href="../utils/debounce.html">debounce</a>
|
|
355
|
+
<a href="../utils/drag.html">drag</a>
|
|
356
|
+
<a href="../utils/elevation.html">elevation</a>
|
|
357
|
+
<a href="../utils/formatTimestamp.html">formatTimestamp</a>
|
|
358
|
+
<a href="../utils/object.html">object</a>
|
|
359
|
+
<a href="../utils/propConverters.html">propConverters</a>
|
|
360
|
+
<a href="../utils/sanitizeHtml.html">sanitizeHtml</a>
|
|
361
|
+
<a href="../utils/string.html">string</a>
|
|
362
|
+
<a href="../utils/theme.html">theme</a>
|
|
363
|
+
<a href="../utils/type.html">type</a>
|
|
364
|
+
<a href="../utils/voice.html">voice</a>
|
|
365
|
+
<a href="../utils/wait.html">wait</a>
|
|
366
|
+
</div>
|
|
367
|
+
</menu>
|
|
368
|
+
</k-aside>
|
|
369
|
+
<style>
|
|
370
|
+
#navSearchList a {
|
|
371
|
+
display: block;
|
|
372
|
+
padding: .25rem .5rem;
|
|
373
|
+
border-radius: var(--radius);
|
|
374
|
+
text-decoration: none;
|
|
375
|
+
color: var(--tc);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
#navSearchList a:hover {
|
|
379
|
+
background: rgba(128, 128, 128, .15);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
#navSearchList a small {
|
|
383
|
+
display: block;
|
|
384
|
+
color: var(--tc_muted);
|
|
385
|
+
}
|
|
386
|
+
</style>
|
|
387
|
+
<script
|
|
388
|
+
src="../src/components/Nav.js"
|
|
389
|
+
type="module"
|
|
390
|
+
></script>
|
|
391
|
+
<script
|
|
392
|
+
src="../src/components/FilterList.js"
|
|
393
|
+
type="module"
|
|
394
|
+
></script>
|
|
395
|
+
<script
|
|
396
|
+
src="../src/components/FilterItem.js"
|
|
397
|
+
type="module"
|
|
398
|
+
></script>
|
|
399
|
+
<script
|
|
400
|
+
src="../src/components/Aside.js"
|
|
401
|
+
type="module"
|
|
402
|
+
></script>
|
|
403
|
+
<script
|
|
404
|
+
src="../src/components/Icon.js"
|
|
405
|
+
type="module"
|
|
406
|
+
></script>
|
|
407
|
+
<script
|
|
408
|
+
src="../src/components/ThemeSwitcher.js"
|
|
409
|
+
type="module"
|
|
410
|
+
></script>
|
|
411
|
+
<script type="module">
|
|
412
|
+
import debounce from '../src/utils/debounce.js';
|
|
413
|
+
|
|
414
|
+
document.getElementById('toggleNavSideMenu').addEventListener('click', async () => {
|
|
415
|
+
await window.customElements.whenDefined('k-aside');
|
|
416
|
+
document.getElementById('navSideMenu').toggle();
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
document.addEventListener('click', function (e) {
|
|
420
|
+
if (e.target.matches('a[href^="#"]')) {
|
|
421
|
+
e.preventDefault();
|
|
422
|
+
const targetId = e.target.getAttribute('href').replace('#', '');
|
|
423
|
+
const target = document.getElementById(targetId);
|
|
424
|
+
if (target) {
|
|
425
|
+
target.scrollIntoView({ behavior: 'smooth' });
|
|
426
|
+
const url = window.location.pathname + window.location.search + '#' + targetId;
|
|
427
|
+
history.replaceState(null, '', url);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
/*
|
|
433
|
+
Nav Search
|
|
434
|
+
*/
|
|
435
|
+
const searchInput = document.getElementById('navSearchInput');
|
|
436
|
+
const searchDropdown = document.getElementById('navSearchDropdown');
|
|
437
|
+
let navSearchList = null;
|
|
438
|
+
|
|
439
|
+
const openSearch = async () => {
|
|
440
|
+
await customElements.whenDefined('k-filter-list');
|
|
441
|
+
if (!navSearchList) navSearchList = document.getElementById('navSearchList');
|
|
442
|
+
navSearchList.filter(searchInput.value);
|
|
443
|
+
searchDropdown.hidden = false;
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const closeSearch = () => {
|
|
447
|
+
searchDropdown.hidden = true;
|
|
448
|
+
navSearchList?.clearFocus();
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
searchInput.addEventListener('focus', openSearch);
|
|
452
|
+
|
|
453
|
+
searchInput.addEventListener('input', debounce(e => {
|
|
454
|
+
navSearchList?.filter(e.target.value);
|
|
455
|
+
}, 150));
|
|
456
|
+
|
|
457
|
+
searchInput.addEventListener('keydown', e => {
|
|
458
|
+
if (e.key === 'Escape') {
|
|
459
|
+
searchInput.value = '';
|
|
460
|
+
searchInput.blur();
|
|
461
|
+
closeSearch();
|
|
462
|
+
} else if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Enter') {
|
|
463
|
+
navSearchList?.handleKeydown(e);
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
searchInput.addEventListener('blur', () => {
|
|
468
|
+
setTimeout(closeSearch, 150);
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
document.addEventListener('keydown', e => {
|
|
472
|
+
let active = document.activeElement;
|
|
473
|
+
while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
|
|
474
|
+
const tag = active?.tagName;
|
|
475
|
+
if (tag === 'INPUT' || tag === 'TEXTAREA' || active?.isContentEditable) return;
|
|
476
|
+
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
|
477
|
+
if (e.key.length === 1) {
|
|
478
|
+
searchInput.focus();
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
</script>
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
<h1 class="ta-center">Chat</h1>
|
|
485
|
+
|
|
486
|
+
<k-main>
|
|
487
|
+
|
|
488
|
+
<k-accordion persistent-id="toc" class="b r mb">
|
|
489
|
+
<k-accordion-header for-panel="toc-panel">Table of Contents</k-accordion-header>
|
|
490
|
+
<k-accordion-panel name="toc-panel">
|
|
491
|
+
<div class="m pl">
|
|
492
|
+
<h6>Examples</h6>
|
|
493
|
+
<a href="#basicUsage">Basic Usage</a><br />
|
|
494
|
+
<a href="#enterBehavior">Enter Behavior</a><br />
|
|
495
|
+
<a href="#showStatus">Message Status</a><br />
|
|
496
|
+
<a href="#disabled">Disabled</a><br />
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
<h6 class="mt"><a href="#jsRef" class="no-link">JavaScript Reference</a></h6>
|
|
500
|
+
<a href="#constructor">Constructor</a><br />
|
|
501
|
+
<a href="#requirements">Requirements</a><br />
|
|
502
|
+
<a href="#properties">Properties</a><br />
|
|
503
|
+
<a href="#methods">Methods</a><br />
|
|
504
|
+
<a href="#cssVariables">CSS Variables</a><br />
|
|
505
|
+
<a href="#events">Events</a><br />
|
|
506
|
+
<a href="#security">Security Notes</a><br />
|
|
507
|
+
</div>
|
|
508
|
+
</k-accordion-panel>
|
|
509
|
+
</k-accordion>
|
|
510
|
+
|
|
511
|
+
<p>A chat-room widget: scrollable history of messages above, a markdown input + Send button below. The input is a <a href="./markdown-editor.html"><code><k-markdown-editor></code></a> — users type markdown (with a Preview tab to see the rendered output before sending), and the message is rendered to HTML via the vendored <a href="https://marked.js.org" target="_blank">marked</a> parser (CommonMark + GFM, including tables and task lists). The rendered HTML runs through <a href="../utils/sanitizeHtml.html"><code>sanitizeHtml</code></a> on its way into the message bubble so any inline HTML the user types is filtered against the allow-list — <code><script></code>, <code><iframe></code>, etc. are always stripped.</p>
|
|
512
|
+
|
|
513
|
+
<h3 id="basicUsage"><a href="#basicUsage" class="no-link">Basic Usage</a></h3>
|
|
514
|
+
<p>The component fires a <code>send</code> event when the user submits. Listen for it and call <code>chat.addMessage(...)</code> for incoming replies. Outgoing messages are added automatically.</p>
|
|
515
|
+
<div class="row -mx">
|
|
516
|
+
<div class="col m-span-12 px">
|
|
517
|
+
<k-card label="HTML">
|
|
518
|
+
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">k-chat</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"basicChat"</span>></span><span class="hljs-tag"></<span class="hljs-name">k-chat</span>></span><br><span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="language-javascript"><br> <span class="hljs-keyword">const</span> chat = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'basicChat'</span>);<br> chat.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'send'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =></span> {<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'Sent:'</span>, e.<span class="hljs-property">detail</span>.<span class="hljs-property">markdown</span>);<br> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {<br> chat.<span class="hljs-title function_">addMessage</span>({<br> <span class="hljs-attr">type</span>: <span class="hljs-string">'incoming'</span>,<br> <span class="hljs-attr">html</span>: <span class="hljs-string">'Got it!'</span>,<br> <span class="hljs-attr">sender</span>: <span class="hljs-string">'Bot'</span><br> });<br> }, <span class="hljs-number">500</span>);<br> });<br></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></code></pre>
|
|
519
|
+
</k-card>
|
|
520
|
+
</div>
|
|
521
|
+
<div class="col m-span-12 px">
|
|
522
|
+
<k-card label="Output">
|
|
523
|
+
<k-chat id="basicChat"></k-chat>
|
|
524
|
+
<script type="module">
|
|
525
|
+
await customElements.whenDefined('k-chat');
|
|
526
|
+
const chat = document.getElementById('basicChat');
|
|
527
|
+
chat.addMessage({ type: 'incoming', html: 'Welcome — say hi!', sender: 'Bot' });
|
|
528
|
+
chat.addEventListener('send', (e) => {
|
|
529
|
+
setTimeout(() => {
|
|
530
|
+
chat.addMessage({
|
|
531
|
+
type: 'incoming',
|
|
532
|
+
html: '<i>Echo:</i> ' + e.detail.html,
|
|
533
|
+
sender: 'Bot'
|
|
534
|
+
});
|
|
535
|
+
}, 500);
|
|
536
|
+
});
|
|
537
|
+
</script>
|
|
538
|
+
</k-card>
|
|
539
|
+
</div>
|
|
540
|
+
</div>
|
|
541
|
+
|
|
542
|
+
<h3 id="enterBehavior"><a href="#enterBehavior" class="no-link">Enter Behavior</a></h3>
|
|
543
|
+
<p>By default the chat matches Slack/Discord/Messages: <strong>Enter sends</strong>, Shift+Enter inserts a newline. Add the <code>enter-newline</code> attribute to flip it — Enter inserts a newline, Shift+Enter sends.</p>
|
|
544
|
+
<div class="row -mx">
|
|
545
|
+
<div class="col m-span-12 px">
|
|
546
|
+
<k-card label="HTML">
|
|
547
|
+
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">k-chat</span> <span class="hljs-attr">enter-newline</span>></span><span class="hljs-tag"></<span class="hljs-name">k-chat</span>></span></code></pre>
|
|
548
|
+
</k-card>
|
|
549
|
+
</div>
|
|
550
|
+
<div class="col m-span-12 px">
|
|
551
|
+
<k-card label="Output">
|
|
552
|
+
<k-chat id="newlineChat" enter-newline></k-chat>
|
|
553
|
+
<script type="module">
|
|
554
|
+
await customElements.whenDefined('k-chat');
|
|
555
|
+
const chat = document.getElementById('newlineChat');
|
|
556
|
+
chat.addMessage({ type: 'incoming', html: 'Enter inserts a newline. Shift+Enter sends.', sender: 'Bot' });
|
|
557
|
+
chat.addEventListener('send', () => {
|
|
558
|
+
setTimeout(() => chat.addMessage({ type: 'incoming', html: '👍', sender: 'Bot' }), 400);
|
|
559
|
+
});
|
|
560
|
+
</script>
|
|
561
|
+
</k-card>
|
|
562
|
+
</div>
|
|
563
|
+
</div>
|
|
564
|
+
|
|
565
|
+
<h3 id="showStatus"><a href="#showStatus" class="no-link">Message Status</a></h3>
|
|
566
|
+
<p>Add <code>show-status</code> and outgoing messages get an iMessage-style status indicator below the bubble. The four statuses are <code>sending</code>, <code>delivered</code>, <code>read</code>, and <code>failed</code>. When the user submits, the message is added with <code>status: 'sending'</code>; advance it via <code>chat.updateMessage(id, { status })</code> as the backend confirms.</p>
|
|
567
|
+
<p>The attribute takes one of two display modes:</p>
|
|
568
|
+
<ul>
|
|
569
|
+
<li><code>show-status</code> or <code>show-status="text"</code> — small grey text labels (<em>Sending…</em>, <em>Delivered</em>, <em>Read</em>, <em>Not Delivered</em>). Default mode.</li>
|
|
570
|
+
<li><code>show-status="icons"</code> — spinner / check / double-check / error icons only.</li>
|
|
571
|
+
</ul>
|
|
572
|
+
<p>Following iMessage convention, the indicator only renders on the <strong>most recent outgoing message</strong> — older delivered/read messages stay clean. <code>sending</code> and <code>failed</code> always show regardless of position so the user knows about in-flight or stuck messages immediately.</p>
|
|
573
|
+
<div class="row -mx">
|
|
574
|
+
<div class="col m-span-12 px">
|
|
575
|
+
<k-card label="HTML">
|
|
576
|
+
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">k-chat</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"ackChat"</span> <span class="hljs-attr">show-status</span>></span><span class="hljs-tag"></<span class="hljs-name">k-chat</span>></span><br><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>></span><span class="language-javascript"><br> <span class="hljs-keyword">await</span> customElements.<span class="hljs-title function_">whenDefined</span>(<span class="hljs-string">'k-chat'</span>);<br> <span class="hljs-keyword">const</span> chat = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'ackChat'</span>);<br> chat.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'send'</span>, <span class="hljs-title function_">async</span> (e) => {<br> <span class="hljs-comment">// Simulate a server round-trip</span><br> <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function"><span class="hljs-params">r</span> =></span> <span class="hljs-built_in">setTimeout</span>(r, <span class="hljs-number">800</span>));<br> chat.<span class="hljs-title function_">updateMessage</span>(e.<span class="hljs-property">detail</span>.<span class="hljs-property">id</span>, {<br> <span class="hljs-attr">status</span>: <span class="hljs-string">'delivered'</span><br> });<br> <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function"><span class="hljs-params">r</span> =></span> <span class="hljs-built_in">setTimeout</span>(r, <span class="hljs-number">1200</span>));<br> chat.<span class="hljs-title function_">updateMessage</span>(e.<span class="hljs-property">detail</span>.<span class="hljs-property">id</span>, {<br> <span class="hljs-attr">status</span>: <span class="hljs-string">'read'</span><br> });<br> });<br></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></code></pre>
|
|
577
|
+
</k-card>
|
|
578
|
+
</div>
|
|
579
|
+
<div class="col m-span-12 px">
|
|
580
|
+
<k-card label="Output">
|
|
581
|
+
<k-chat id="ackChat" show-status></k-chat>
|
|
582
|
+
<script type="module">
|
|
583
|
+
await customElements.whenDefined('k-chat');
|
|
584
|
+
const chat = document.getElementById('ackChat');
|
|
585
|
+
chat.addEventListener('send', async (e) => {
|
|
586
|
+
await new Promise(r => setTimeout(r, 800));
|
|
587
|
+
chat.updateMessage(e.detail.id, { status: 'delivered' });
|
|
588
|
+
await new Promise(r => setTimeout(r, 1200));
|
|
589
|
+
chat.updateMessage(e.detail.id, { status: 'read' });
|
|
590
|
+
});
|
|
591
|
+
</script>
|
|
592
|
+
</k-card>
|
|
593
|
+
</div>
|
|
594
|
+
</div>
|
|
595
|
+
|
|
596
|
+
<h4>Icon Mode</h4>
|
|
597
|
+
<p>Pass <code>show-status="icons"</code> for compact icon-only badges (spinner / single check / double check / red error) instead of text labels — closer to WhatsApp / Telegram styling.</p>
|
|
598
|
+
<div class="row -mx">
|
|
599
|
+
<div class="col m-span-12 px">
|
|
600
|
+
<k-card label="HTML">
|
|
601
|
+
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">k-chat</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"iconChat"</span> <span class="hljs-attr">show-status</span>=<span class="hljs-string">"icons"</span>></span><span class="hljs-tag"></<span class="hljs-name">k-chat</span>></span><br><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"module"</span>></span><span class="language-javascript"><br> <span class="hljs-keyword">await</span> customElements.<span class="hljs-title function_">whenDefined</span>(<span class="hljs-string">'k-chat'</span>);<br> <span class="hljs-keyword">const</span> chat = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'iconChat'</span>);<br> chat.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'send'</span>, <span class="hljs-title function_">async</span> (e) => {<br> <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function"><span class="hljs-params">r</span> =></span> <span class="hljs-built_in">setTimeout</span>(r, <span class="hljs-number">800</span>));<br> chat.<span class="hljs-title function_">updateMessage</span>(e.<span class="hljs-property">detail</span>.<span class="hljs-property">id</span>, {<br> <span class="hljs-attr">status</span>: <span class="hljs-string">'delivered'</span><br> });<br> <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Promise</span>(<span class="hljs-function"><span class="hljs-params">r</span> =></span> <span class="hljs-built_in">setTimeout</span>(r, <span class="hljs-number">1200</span>));<br> chat.<span class="hljs-title function_">updateMessage</span>(e.<span class="hljs-property">detail</span>.<span class="hljs-property">id</span>, {<br> <span class="hljs-attr">status</span>: <span class="hljs-string">'read'</span><br> });<br> });<br></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></code></pre>
|
|
602
|
+
</k-card>
|
|
603
|
+
</div>
|
|
604
|
+
<div class="col m-span-12 px">
|
|
605
|
+
<k-card label="Output">
|
|
606
|
+
<k-chat id="iconChat" show-status="icons"></k-chat>
|
|
607
|
+
<script type="module">
|
|
608
|
+
await customElements.whenDefined('k-chat');
|
|
609
|
+
const chat = document.getElementById('iconChat');
|
|
610
|
+
chat.addEventListener('send', async (e) => {
|
|
611
|
+
await new Promise(r => setTimeout(r, 800));
|
|
612
|
+
chat.updateMessage(e.detail.id, { status: 'delivered' });
|
|
613
|
+
await new Promise(r => setTimeout(r, 1200));
|
|
614
|
+
chat.updateMessage(e.detail.id, { status: 'read' });
|
|
615
|
+
});
|
|
616
|
+
</script>
|
|
617
|
+
</k-card>
|
|
618
|
+
</div>
|
|
619
|
+
</div>
|
|
620
|
+
|
|
621
|
+
<h3 id="disabled"><a href="#disabled" class="no-link">Disabled</a></h3>
|
|
622
|
+
<p>The <code>disabled</code> attribute prevents user interaction. Useful while waiting for connection or rate-limiting.</p>
|
|
623
|
+
<div class="row -mx">
|
|
624
|
+
<div class="col m-span-12 px">
|
|
625
|
+
<k-card label="HTML">
|
|
626
|
+
<pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">k-chat</span> <span class="hljs-attr">disabled</span>></span><span class="hljs-tag"></<span class="hljs-name">k-chat</span>></span></code></pre>
|
|
627
|
+
</k-card>
|
|
628
|
+
</div>
|
|
629
|
+
<div class="col m-span-12 px">
|
|
630
|
+
<k-card label="Output">
|
|
631
|
+
<k-chat disabled></k-chat>
|
|
632
|
+
</k-card>
|
|
633
|
+
</div>
|
|
634
|
+
</div>
|
|
635
|
+
|
|
636
|
+
<h2 id="jsRef"><a href="#jsRef" class="no-link">JavaScript Reference</a></h2>
|
|
637
|
+
|
|
638
|
+
<h3 id="constructor"><a href="#constructor" class="no-link">Constructor</a></h3>
|
|
639
|
+
<h6>Extends <a href="./shadow-component.html">ShadowComponent</a></h6>
|
|
640
|
+
<h5><code>new Chat()</code></h5>
|
|
641
|
+
|
|
642
|
+
<h3 id="requirements"><a href="#requirements" class="no-link">Requirements</a></h3>
|
|
643
|
+
<ul>
|
|
644
|
+
<li><a href="./shadow-component.html">ShadowComponent</a></li>
|
|
645
|
+
<li><a href="./markdown-editor.html">MarkdownEditor</a> (used internally for the input)</li>
|
|
646
|
+
<li><a href="../utils/sanitizeHtml.html">sanitizeHtml</a> utility</li>
|
|
647
|
+
</ul>
|
|
648
|
+
|
|
649
|
+
<h3 id="properties"><a href="#properties" class="no-link">Properties</a></h3>
|
|
650
|
+
<h5><code>enterNewline<i>: Boolean</i></code></h5>
|
|
651
|
+
<p>Inverts the default Enter-key behavior. Default is <code>false</code> (Slack-style: Enter sends, Shift+Enter inserts a newline). When <code>true</code>: Enter inserts a newline and Shift+Enter sends. Syncs to <code>enter-newline</code> attribute.</p>
|
|
652
|
+
|
|
653
|
+
<h5><code>showStatus<i>: String</i></code></h5>
|
|
654
|
+
<p>Enables and configures the per-message status indicator. Possible values: <code>null</code>/empty (off, default), <code>"text"</code> (small grey labels — the default mode when the attribute is set without a value), or <code>"icons"</code> (spinner/check/double-check/error glyphs). The indicator only appears on the most recent outgoing message, except for <code>sending</code> and <code>failed</code> which always show. Outgoing messages submitted via the input start as <code>sending</code> — advance them via <code>updateMessage(id, { status })</code> as the backend confirms. Syncs to <code>show-status</code> attribute.</p>
|
|
655
|
+
|
|
656
|
+
<h5><code>placeholder<i>: String</i></code></h5>
|
|
657
|
+
<p>Placeholder text for the input. Default <code>"Type a message..."</code>. Syncs to <code>placeholder</code> attribute.</p>
|
|
658
|
+
|
|
659
|
+
<h5><code>disabled<i>: Boolean</i></code></h5>
|
|
660
|
+
<p>When <code>true</code>, the input and Send button are disabled. Syncs to <code>disabled</code> attribute.</p>
|
|
661
|
+
|
|
662
|
+
<h5><code>messages<i>: Array</i></code> (read-only state)</h5>
|
|
663
|
+
<p>The current array of messages. Each entry is <code>{ id, type, html, status, sender, timestamp }</code>. Don't mutate directly — use the public methods.</p>
|
|
664
|
+
|
|
665
|
+
<h3 id="methods"><a href="#methods" class="no-link">Methods</a></h3>
|
|
666
|
+
<h5><code>addMessage(msg)<i>: string</i></code></h5>
|
|
667
|
+
<p>Append a message and return its <code>id</code>. <code>msg</code> object accepts <code>{ id, type, html, status, sender, timestamp }</code>; missing fields are filled in (auto-generated id, type defaults to <code>"incoming"</code>, etc.). HTML is sanitized.</p>
|
|
668
|
+
|
|
669
|
+
<h5><code>updateMessage(id, updates)<i>: boolean</i></code></h5>
|
|
670
|
+
<p>Update an existing message's <code>html</code>, <code>status</code>, <code>sender</code>, or <code>timestamp</code>. Returns <code>true</code> if a message with that id was found.</p>
|
|
671
|
+
|
|
672
|
+
<h5><code>removeMessage(id)<i>: boolean</i></code></h5>
|
|
673
|
+
<p>Remove a message. Returns <code>true</code> if found.</p>
|
|
674
|
+
|
|
675
|
+
<h5><code>clear()<i>: void</i></code></h5>
|
|
676
|
+
<p>Remove every message.</p>
|
|
677
|
+
|
|
678
|
+
<h5><code>send()<i>: string | null</i></code></h5>
|
|
679
|
+
<p>Programmatically submit whatever is currently in the input. Returns the new outgoing message's <code>id</code>, or <code>null</code> if the input was empty.</p>
|
|
680
|
+
|
|
681
|
+
<h3 id="cssVariables"><a href="#cssVariables" class="no-link">CSS Variables</a></h3>
|
|
682
|
+
<table class="b">
|
|
683
|
+
<thead><tr><th>Variable</th><th>Default</th><th>Description</th></tr></thead>
|
|
684
|
+
<tbody>
|
|
685
|
+
<tr><td><code>--window_min_height</code></td><td><code>16rem</code></td><td>Minimum height of the message window (the chat won't collapse below this even when empty)</td></tr>
|
|
686
|
+
<tr><td><code>--window_max_height</code></td><td><code>32rem</code></td><td>Maximum height of the message window before it scrolls internally</td></tr>
|
|
687
|
+
<tr><td><code>--chat_padding</code></td><td><code>0.5rem</code></td><td>Padding inside both the message window and the input row</td></tr>
|
|
688
|
+
<tr><td><code>--chat_gap</code></td><td><code>0.5rem</code></td><td>Gap between messages and gap between the editor and the Send button</td></tr>
|
|
689
|
+
<tr><td><code>--height</code></td><td><code>13rem</code></td><td>Initial height of the embedded markdown editor; the user can drag the resize handle below it to grow or shrink past this</td></tr>
|
|
690
|
+
<tr><td><code>--message_max_width</code></td><td><code>75%</code></td><td>Max width of a single message bubble</td></tr>
|
|
691
|
+
<tr><td><code>--message_radius</code></td><td><code>1rem</code></td><td>Border radius of message bubbles</td></tr>
|
|
692
|
+
<tr><td><code>--bubble_padding</code></td><td><code>0.5rem 0.75rem</code></td><td>Padding inside bubbles</td></tr>
|
|
693
|
+
<tr><td><code>--bubble_bg__incoming</code></td><td><code>var(--c_bg__alt)</code></td><td>Background color of incoming bubbles</td></tr>
|
|
694
|
+
<tr><td><code>--bubble_bg__outgoing</code></td><td><code>var(--c_primary)</code></td><td>Background color of outgoing bubbles</td></tr>
|
|
695
|
+
<tr><td><code>--bubble_tc__outgoing</code></td><td><code>white</code></td><td>Text color of outgoing bubbles</td></tr>
|
|
696
|
+
</tbody>
|
|
697
|
+
</table>
|
|
698
|
+
|
|
699
|
+
<h3 id="events"><a href="#events" class="no-link">Events</a></h3>
|
|
700
|
+
<h5><code>send</code></h5>
|
|
701
|
+
<p>Fires when the user submits. <code>detail</code> contains <code>{ id, html, markdown }</code> — <code>html</code> is the rendered+sanitized output (the same string used in the bubble) and <code>markdown</code> is the raw source the user typed. The outgoing message is already in the conversation by the time the event fires — if you need to roll it back (e.g. on a network failure), call <code>updateMessage(id, { status: 'failed' })</code> or <code>removeMessage(id)</code>.</p>
|
|
702
|
+
|
|
703
|
+
<h3 id="security"><a href="#security" class="no-link">Security Notes</a></h3>
|
|
704
|
+
<div class="callout warning b r p mb">
|
|
705
|
+
<strong>The frontend sanitization in this component is not a substitute for server-side sanitization.</strong>
|
|
706
|
+
</div>
|
|
707
|
+
<p>Every message that enters the chat (whether from the user's editor, from <code>addMessage()</code>, or from <code>updateMessage()</code>) runs through <a href="../utils/sanitizeHtml.html"><code>sanitizeHtml</code></a> before it is added to the DOM. That strips <code><script></code>, <code><style></code>, <code><iframe></code>, event-handler attributes, and unsafe URL schemes — but it only protects content that flows through this component.</p>
|
|
708
|
+
<p>If you persist messages on a server and rebroadcast them to other users, the server <strong>must</strong> independently sanitize before storing and again before sending. A determined attacker can submit raw HTTP without ever loading your client code.</p>
|
|
709
|
+
|
|
710
|
+
</k-main>
|
|
711
|
+
<div style="height:33vh"></div>
|
|
712
|
+
<script type="module" src="../src/components/Main.js"></script>
|
|
713
|
+
|
|
714
|
+
<script type="module" src="../src/components/Chat.js"></script>
|
|
715
|
+
<script type="module" src="../src/components/Accordion.js"></script>
|
|
716
|
+
<script type="module" src="../src/components/Card.js"></script>
|
|
717
|
+
|
|
718
|
+
</body>
|
|
719
|
+
</html>
|