@sveltia/ui 0.20.2 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/{package → dist}/components/button/button.svelte +1 -1
  2. package/{package → dist}/components/button/select-button-group.svelte +3 -0
  3. package/{package → dist}/components/calendar/calendar.svelte +1 -1
  4. package/{package → dist}/components/checkbox/checkbox.svelte +9 -2
  5. package/{package → dist}/components/grid/grid.svelte +1 -1
  6. package/{package → dist}/components/listbox/listbox.svelte +3 -0
  7. package/{package → dist}/components/menu/menu-button.svelte +1 -1
  8. package/{package → dist}/components/progressbar/progressbar.svelte +4 -1
  9. package/{package → dist}/components/radio/radio-group.svelte +9 -0
  10. package/{package → dist}/components/radio/radio.svelte +3 -3
  11. package/{package → dist}/components/select/combobox.svelte +6 -2
  12. package/{package → dist}/components/select/select-tags.svelte +1 -1
  13. package/{package → dist}/components/slider/slider.svelte +14 -7
  14. package/{package → dist}/components/switch/switch.svelte +4 -1
  15. package/{package → dist}/components/tabs/tab-list.svelte +1 -1
  16. package/{package → dist}/components/text-editor/core.d.ts +3 -1
  17. package/{package → dist}/components/text-editor/core.js +113 -19
  18. package/{package → dist}/components/text-editor/index.js +6 -0
  19. package/dist/components/text-editor/lexical-root.svelte +288 -0
  20. package/{package → dist}/components/text-editor/text-editor.svelte +18 -9
  21. package/{package → dist}/components/text-editor/text-editor.svelte.d.ts +8 -0
  22. package/{package → dist}/components/text-editor/toolbar/editor-toolbar.svelte +15 -0
  23. package/dist/components/text-editor/toolbar/insert-image-button.svelte +43 -0
  24. package/dist/components/text-editor/toolbar/insert-image-button.svelte.d.ts +17 -0
  25. package/dist/components/text-editor/toolbar/insert-menu-button.svelte +53 -0
  26. package/dist/components/text-editor/toolbar/insert-menu-button.svelte.d.ts +17 -0
  27. package/{package → dist}/components/text-editor/toolbar/toggle-block-menu-item.svelte +7 -0
  28. package/{package → dist}/components/text-field/number-input.svelte +31 -16
  29. package/{package → dist}/components/text-field/number-input.svelte.d.ts +8 -0
  30. package/{package → dist}/components/text-field/password-input.svelte +1 -0
  31. package/{package → dist}/components/text-field/password-input.svelte.d.ts +8 -0
  32. package/{package → dist}/components/text-field/search-bar.svelte +1 -0
  33. package/{package → dist}/components/text-field/search-bar.svelte.d.ts +8 -0
  34. package/{package → dist}/components/text-field/text-area.svelte +4 -4
  35. package/{package → dist}/components/text-field/text-input.svelte +7 -2
  36. package/{package → dist}/components/text-field/text-input.svelte.d.ts +13 -2
  37. package/{package → dist}/components/toast/toast.svelte +1 -1
  38. package/{package → dist}/components/toolbar/toolbar.svelte +2 -2
  39. package/{package → dist}/components/util/app-shell.svelte +6 -6
  40. package/{package → dist}/components/util/app-shell.svelte.d.ts +2 -2
  41. package/{package → dist}/components/util/popup.svelte +2 -2
  42. package/{package → dist}/locales/en.d.ts +1 -0
  43. package/{package → dist}/locales/en.js +1 -0
  44. package/{package → dist}/locales/ja.d.ts +1 -0
  45. package/{package → dist}/locales/ja.js +1 -0
  46. package/{package → dist}/styles/variables.scss +2 -2
  47. package/{package → dist}/typedefs.d.ts +32 -5
  48. package/{package → dist}/typedefs.js +26 -14
  49. package/package.json +20 -19
  50. package/package/components/text-editor/lexical-root.svelte +0 -156
  51. /package/{package → dist}/components/alert/alert.svelte +0 -0
  52. /package/{package → dist}/components/alert/alert.svelte.d.ts +0 -0
  53. /package/{package → dist}/components/button/button-group.svelte +0 -0
  54. /package/{package → dist}/components/button/button-group.svelte.d.ts +0 -0
  55. /package/{package → dist}/components/button/button.svelte.d.ts +0 -0
  56. /package/{package → dist}/components/button/select-button-group.svelte.d.ts +0 -0
  57. /package/{package → dist}/components/button/select-button.svelte +0 -0
  58. /package/{package → dist}/components/button/select-button.svelte.d.ts +0 -0
  59. /package/{package → dist}/components/button/split-button.svelte +0 -0
  60. /package/{package → dist}/components/button/split-button.svelte.d.ts +0 -0
  61. /package/{package → dist}/components/calendar/calendar.svelte.d.ts +0 -0
  62. /package/{package → dist}/components/checkbox/checkbox-group.svelte +0 -0
  63. /package/{package → dist}/components/checkbox/checkbox-group.svelte.d.ts +0 -0
  64. /package/{package → dist}/components/checkbox/checkbox.svelte.d.ts +0 -0
  65. /package/{package → dist}/components/dialog/alert-dialog.svelte +0 -0
  66. /package/{package → dist}/components/dialog/alert-dialog.svelte.d.ts +0 -0
  67. /package/{package → dist}/components/dialog/confirmation-dialog.svelte +0 -0
  68. /package/{package → dist}/components/dialog/confirmation-dialog.svelte.d.ts +0 -0
  69. /package/{package → dist}/components/dialog/dialog.svelte +0 -0
  70. /package/{package → dist}/components/dialog/dialog.svelte.d.ts +0 -0
  71. /package/{package → dist}/components/dialog/prompt-dialog.svelte +0 -0
  72. /package/{package → dist}/components/dialog/prompt-dialog.svelte.d.ts +0 -0
  73. /package/{package → dist}/components/disclosure/disclosure.svelte +0 -0
  74. /package/{package → dist}/components/disclosure/disclosure.svelte.d.ts +0 -0
  75. /package/{package → dist}/components/divider/divider.svelte +0 -0
  76. /package/{package → dist}/components/divider/divider.svelte.d.ts +0 -0
  77. /package/{package → dist}/components/divider/spacer.svelte +0 -0
  78. /package/{package → dist}/components/divider/spacer.svelte.d.ts +0 -0
  79. /package/{package → dist}/components/drawer/drawer.svelte +0 -0
  80. /package/{package → dist}/components/drawer/drawer.svelte.d.ts +0 -0
  81. /package/{package → dist}/components/grid/grid-body.svelte +0 -0
  82. /package/{package → dist}/components/grid/grid-body.svelte.d.ts +0 -0
  83. /package/{package → dist}/components/grid/grid-cell.svelte +0 -0
  84. /package/{package → dist}/components/grid/grid-cell.svelte.d.ts +0 -0
  85. /package/{package → dist}/components/grid/grid-col-header.svelte +0 -0
  86. /package/{package → dist}/components/grid/grid-col-header.svelte.d.ts +0 -0
  87. /package/{package → dist}/components/grid/grid-foot.svelte +0 -0
  88. /package/{package → dist}/components/grid/grid-foot.svelte.d.ts +0 -0
  89. /package/{package → dist}/components/grid/grid-head.svelte +0 -0
  90. /package/{package → dist}/components/grid/grid-head.svelte.d.ts +0 -0
  91. /package/{package → dist}/components/grid/grid-row-header.svelte +0 -0
  92. /package/{package → dist}/components/grid/grid-row-header.svelte.d.ts +0 -0
  93. /package/{package → dist}/components/grid/grid-row.svelte +0 -0
  94. /package/{package → dist}/components/grid/grid-row.svelte.d.ts +0 -0
  95. /package/{package → dist}/components/grid/grid.svelte.d.ts +0 -0
  96. /package/{package → dist}/components/icon/icon.svelte +0 -0
  97. /package/{package → dist}/components/icon/icon.svelte.d.ts +0 -0
  98. /package/{package → dist}/components/listbox/listbox.svelte.d.ts +0 -0
  99. /package/{package → dist}/components/listbox/option-group.svelte +0 -0
  100. /package/{package → dist}/components/listbox/option-group.svelte.d.ts +0 -0
  101. /package/{package → dist}/components/listbox/option.svelte +0 -0
  102. /package/{package → dist}/components/listbox/option.svelte.d.ts +0 -0
  103. /package/{package → dist}/components/menu/menu-button.svelte.d.ts +0 -0
  104. /package/{package → dist}/components/menu/menu-item-checkbox.svelte +0 -0
  105. /package/{package → dist}/components/menu/menu-item-checkbox.svelte.d.ts +0 -0
  106. /package/{package → dist}/components/menu/menu-item-group.svelte +0 -0
  107. /package/{package → dist}/components/menu/menu-item-group.svelte.d.ts +0 -0
  108. /package/{package → dist}/components/menu/menu-item-radio.svelte +0 -0
  109. /package/{package → dist}/components/menu/menu-item-radio.svelte.d.ts +0 -0
  110. /package/{package → dist}/components/menu/menu-item.svelte +0 -0
  111. /package/{package → dist}/components/menu/menu-item.svelte.d.ts +0 -0
  112. /package/{package → dist}/components/menu/menu.svelte +0 -0
  113. /package/{package → dist}/components/menu/menu.svelte.d.ts +0 -0
  114. /package/{package → dist}/components/progressbar/progressbar.svelte.d.ts +0 -0
  115. /package/{package → dist}/components/radio/radio-group.svelte.d.ts +0 -0
  116. /package/{package → dist}/components/radio/radio.svelte.d.ts +0 -0
  117. /package/{package → dist}/components/select/combobox.svelte.d.ts +0 -0
  118. /package/{package → dist}/components/select/select-tags.svelte.d.ts +0 -0
  119. /package/{package → dist}/components/select/select.svelte +0 -0
  120. /package/{package → dist}/components/select/select.svelte.d.ts +0 -0
  121. /package/{package → dist}/components/slider/slider.svelte.d.ts +0 -0
  122. /package/{package → dist}/components/switch/switch.svelte.d.ts +0 -0
  123. /package/{package → dist}/components/table/table-body.svelte +0 -0
  124. /package/{package → dist}/components/table/table-body.svelte.d.ts +0 -0
  125. /package/{package → dist}/components/table/table-cell.svelte +0 -0
  126. /package/{package → dist}/components/table/table-cell.svelte.d.ts +0 -0
  127. /package/{package → dist}/components/table/table-col-header.svelte +0 -0
  128. /package/{package → dist}/components/table/table-col-header.svelte.d.ts +0 -0
  129. /package/{package → dist}/components/table/table-foot.svelte +0 -0
  130. /package/{package → dist}/components/table/table-foot.svelte.d.ts +0 -0
  131. /package/{package → dist}/components/table/table-head.svelte +0 -0
  132. /package/{package → dist}/components/table/table-head.svelte.d.ts +0 -0
  133. /package/{package → dist}/components/table/table-row-header.svelte +0 -0
  134. /package/{package → dist}/components/table/table-row-header.svelte.d.ts +0 -0
  135. /package/{package → dist}/components/table/table-row.svelte +0 -0
  136. /package/{package → dist}/components/table/table-row.svelte.d.ts +0 -0
  137. /package/{package → dist}/components/table/table.svelte +0 -0
  138. /package/{package → dist}/components/table/table.svelte.d.ts +0 -0
  139. /package/{package → dist}/components/tabs/tab-box.svelte +0 -0
  140. /package/{package → dist}/components/tabs/tab-box.svelte.d.ts +0 -0
  141. /package/{package → dist}/components/tabs/tab-list.svelte.d.ts +0 -0
  142. /package/{package → dist}/components/tabs/tab-panel.svelte +0 -0
  143. /package/{package → dist}/components/tabs/tab-panel.svelte.d.ts +0 -0
  144. /package/{package → dist}/components/tabs/tab-panels.svelte +0 -0
  145. /package/{package → dist}/components/tabs/tab-panels.svelte.d.ts +0 -0
  146. /package/{package → dist}/components/tabs/tab.svelte +0 -0
  147. /package/{package → dist}/components/tabs/tab.svelte.d.ts +0 -0
  148. /package/{package → dist}/components/text-editor/index.d.ts +0 -0
  149. /package/{package → dist}/components/text-editor/lexical-root.svelte.d.ts +0 -0
  150. /package/{package → dist}/components/text-editor/toolbar/editor-toolbar.svelte.d.ts +0 -0
  151. /package/{package → dist}/components/text-editor/toolbar/format-text-button.svelte +0 -0
  152. /package/{package → dist}/components/text-editor/toolbar/format-text-button.svelte.d.ts +0 -0
  153. /package/{package → dist}/components/text-editor/toolbar/insert-link-button.svelte +0 -0
  154. /package/{package → dist}/components/text-editor/toolbar/insert-link-button.svelte.d.ts +0 -0
  155. /package/{package → dist}/components/text-editor/toolbar/toggle-block-menu-item.svelte.d.ts +0 -0
  156. /package/{package → dist}/components/text-field/text-area.svelte.d.ts +0 -0
  157. /package/{package → dist}/components/toast/toast.svelte.d.ts +0 -0
  158. /package/{package → dist}/components/toolbar/toolbar.svelte.d.ts +0 -0
  159. /package/{package → dist}/components/util/group.svelte +0 -0
  160. /package/{package → dist}/components/util/group.svelte.d.ts +0 -0
  161. /package/{package → dist}/components/util/modal.svelte +0 -0
  162. /package/{package → dist}/components/util/modal.svelte.d.ts +0 -0
  163. /package/{package → dist}/components/util/placeholder.svelte +0 -0
  164. /package/{package → dist}/components/util/placeholder.svelte.d.ts +0 -0
  165. /package/{package → dist}/components/util/popup.svelte.d.ts +0 -0
  166. /package/{package → dist}/index.d.ts +0 -0
  167. /package/{package → dist}/index.js +0 -0
  168. /package/{package → dist}/services/events.svelte.d.ts +0 -0
  169. /package/{package → dist}/services/events.svelte.js +0 -0
  170. /package/{package → dist}/services/group.svelte.d.ts +0 -0
  171. /package/{package → dist}/services/group.svelte.js +0 -0
  172. /package/{package → dist}/services/popup.svelte.d.ts +0 -0
  173. /package/{package → dist}/services/popup.svelte.js +0 -0
  174. /package/{package → dist}/styles/core.scss +0 -0
@@ -0,0 +1,288 @@
1
+ <script>
2
+ import { getContext, onMount } from 'svelte';
3
+
4
+ /**
5
+ * @typedef {object} Props
6
+ * @property {string} [value] - Input value.
7
+ * @property {string} [class] - The `class` attribute on the wrapper element.
8
+ * @property {boolean} [hidden] - Whether to hide the widget.
9
+ * @property {boolean} [disabled] - Whether to disable the widget. An alias of the `aria-disabled`
10
+ * attribute.
11
+ * @property {boolean} [readonly] - Whether to make the widget read-only. An alias of the
12
+ * `aria-readonly` attribute.
13
+ * @property {boolean} [required] - Whether to mark the widget required. An alias of the
14
+ * `aria-required` attribute.
15
+ * @property {boolean} [invalid] - Whether to mark the widget invalid. An alias of the
16
+ * `aria-invalid` attribute.
17
+ * @property {import('svelte').Snippet} [children] - Primary slot content.
18
+ */
19
+
20
+ /**
21
+ * @type {Props & Record<string, any>}
22
+ */
23
+ let {
24
+ /* eslint-disable prefer-const */
25
+ value = $bindable(),
26
+ class: className,
27
+ hidden = false,
28
+ disabled = false,
29
+ readonly = false,
30
+ required = false,
31
+ invalid = false,
32
+ children,
33
+ ...restProps
34
+ /* eslint-enable prefer-const */
35
+ } = $props();
36
+
37
+ /**
38
+ * Text editor state.
39
+ * @type {import('../../typedefs').TextEditorState}
40
+ */
41
+ const { editor, editorId, selectionBlockType, selectionInlineTypes, hasConverterError } =
42
+ getContext('state');
43
+
44
+ /**
45
+ * Reference to the Lexical editor root element.
46
+ * @type {HTMLElement | undefined}
47
+ */
48
+ let lexicalRoot = $state();
49
+
50
+ const editable = $derived(!(disabled || readonly));
51
+
52
+ $effect(() => {
53
+ $editor?.setEditable(editable);
54
+ });
55
+
56
+ /**
57
+ * Update {@link value} and other state variables whenever the editor content is updated.
58
+ * @param {Event} event - `Update` custom event.
59
+ */
60
+ const onUpdate = (event) => {
61
+ if ($hasConverterError) {
62
+ return;
63
+ }
64
+
65
+ const { detail } = /** @type {CustomEvent} */ (event);
66
+ const newValue = detail.value;
67
+
68
+ if (value !== newValue) {
69
+ value = newValue;
70
+ }
71
+
72
+ $selectionBlockType = detail.selectionBlockType;
73
+ $selectionInlineTypes = detail.selectionInlineTypes;
74
+ };
75
+
76
+ /**
77
+ * Listen to `click` events on the editor. Ignore a click on a link.
78
+ * @param {MouseEvent} event - `click` event.
79
+ */
80
+ const onClick = (event) => {
81
+ if (/** @type {HTMLElement} */ (event.target)?.matches('a')) {
82
+ event.preventDefault();
83
+ }
84
+ };
85
+
86
+ onMount(() => {
87
+ lexicalRoot?.addEventListener('Update', onUpdate);
88
+ lexicalRoot?.addEventListener('click', onClick);
89
+
90
+ return () => {
91
+ lexicalRoot?.removeEventListener('Update', onUpdate);
92
+ lexicalRoot?.removeEventListener('click', onClick);
93
+ };
94
+ });
95
+
96
+ $effect(() => {
97
+ if ($editor && lexicalRoot) {
98
+ $editor.setRootElement(lexicalRoot);
99
+ }
100
+ });
101
+ </script>
102
+
103
+ <div
104
+ bind:this={lexicalRoot}
105
+ {...restProps}
106
+ role="textbox"
107
+ aria-multiline="true"
108
+ aria-hidden={hidden}
109
+ aria-disabled={disabled}
110
+ aria-readonly={readonly}
111
+ aria-required={required}
112
+ aria-invalid={invalid}
113
+ class="lexical-root"
114
+ id="{$editorId}-lexical-root"
115
+ contenteditable={editable}
116
+ {hidden}
117
+ ></div>
118
+
119
+ <style>.lexical-root {
120
+ border: 1px solid var(--sui-textbox-border-color);
121
+ border-radius: 0 0 var(--sui-textbox-border-radius) var(--sui-textbox-border-radius) !important;
122
+ padding: var(--sui-textbox-multiline-padding);
123
+ min-height: 8em;
124
+ color: var(--sui-textbox-foreground-color);
125
+ background-color: var(--sui-textbox-background-color);
126
+ font-family: var(--sui-textbox-font-family);
127
+ font-size: var(--sui-textbox-font-size);
128
+ line-height: var(--sui-textbox-multiline-line-height);
129
+ }
130
+ .lexical-root:focus-visible {
131
+ outline: 0;
132
+ }
133
+ .lexical-root[aria-invalid=true] {
134
+ border-color: var(--sui-error-border-color);
135
+ }
136
+ .lexical-root > :global(:first-child) {
137
+ margin-top: 0;
138
+ }
139
+ .lexical-root > :global(:last-child) {
140
+ margin-bottom: 0;
141
+ }
142
+ .lexical-root :global(strong.italic) {
143
+ font-style: italic;
144
+ }
145
+ .lexical-root :global(li.nested) {
146
+ list-style-type: none;
147
+ }
148
+ .lexical-root :global(.code-block) {
149
+ position: relative;
150
+ display: block;
151
+ padding: 8px 8px 8px 56px;
152
+ background-color: var(--sui-code-background-color);
153
+ overflow-x: auto;
154
+ white-space: pre;
155
+ }
156
+ .lexical-root :global(.code-block:not(:first-child)) {
157
+ margin-top: 1em;
158
+ }
159
+ .lexical-root :global(.code-block:not(:last-child)) {
160
+ margin-bottom: 1em;
161
+ }
162
+ .lexical-root :global(.code-block::before) {
163
+ position: absolute;
164
+ inset: 0 auto 0 0;
165
+ content: attr(data-gutter);
166
+ padding: 8px;
167
+ min-width: 40px;
168
+ color: var(--sui-tertiary-foreground-color);
169
+ background-color: var(--sui-secondary-background-color);
170
+ text-align: right;
171
+ }
172
+ .lexical-root :global([data-lexical-text=true]) {
173
+ cursor: text;
174
+ }
175
+
176
+ :root[data-theme=light] .lexical-root :global(.token.comment),
177
+ :root[data-theme=light] .lexical-root :global(.token.prolog),
178
+ :root[data-theme=light] .lexical-root :global(.token.doctype),
179
+ :root[data-theme=light] .lexical-root :global(.token.cdata) {
180
+ color: slategray;
181
+ }
182
+ :root[data-theme=light] .lexical-root :global(.token.punctuation) {
183
+ color: #999;
184
+ }
185
+ :root[data-theme=light] .lexical-root :global(.token.namespace) {
186
+ opacity: 0.7;
187
+ }
188
+ :root[data-theme=light] .lexical-root :global(.token.property),
189
+ :root[data-theme=light] .lexical-root :global(.token.tag),
190
+ :root[data-theme=light] .lexical-root :global(.token.boolean),
191
+ :root[data-theme=light] .lexical-root :global(.token.number),
192
+ :root[data-theme=light] .lexical-root :global(.token.constant),
193
+ :root[data-theme=light] .lexical-root :global(.token.symbol),
194
+ :root[data-theme=light] .lexical-root :global(.token.deleted) {
195
+ color: #905;
196
+ }
197
+ :root[data-theme=light] .lexical-root :global(.token.selector),
198
+ :root[data-theme=light] .lexical-root :global(.token.attr-name),
199
+ :root[data-theme=light] .lexical-root :global(.token.string),
200
+ :root[data-theme=light] .lexical-root :global(.token.char),
201
+ :root[data-theme=light] .lexical-root :global(.token.builtin),
202
+ :root[data-theme=light] .lexical-root :global(.token.inserted) {
203
+ color: #690;
204
+ }
205
+ :root[data-theme=light] .lexical-root :global(.token.operator),
206
+ :root[data-theme=light] .lexical-root :global(.token.entity),
207
+ :root[data-theme=light] .lexical-root :global(.token.url),
208
+ :root[data-theme=light] .lexical-root :global(.language-css) :global(.token.string),
209
+ :root[data-theme=light] .lexical-root :global(.style) :global(.token.string) {
210
+ color: #9a6e3a;
211
+ }
212
+ :root[data-theme=light] .lexical-root :global(.token.atrule),
213
+ :root[data-theme=light] .lexical-root :global(.token.attr-value),
214
+ :root[data-theme=light] .lexical-root :global(.token.keyword) {
215
+ color: #07a;
216
+ }
217
+ :root[data-theme=light] .lexical-root :global(.token.function),
218
+ :root[data-theme=light] .lexical-root :global(.token.class-name) {
219
+ color: #dd4a68;
220
+ }
221
+ :root[data-theme=light] .lexical-root :global(.token.regex),
222
+ :root[data-theme=light] .lexical-root :global(.token.important),
223
+ :root[data-theme=light] .lexical-root :global(.token.variable) {
224
+ color: #e90;
225
+ }
226
+
227
+ :root[data-theme=dark] .lexical-root :global(.token.comment),
228
+ :root[data-theme=dark] .lexical-root :global(.token.block-comment),
229
+ :root[data-theme=dark] .lexical-root :global(.token.prolog),
230
+ :root[data-theme=dark] .lexical-root :global(.token.doctype),
231
+ :root[data-theme=dark] .lexical-root :global(.token.cdata) {
232
+ color: #999;
233
+ }
234
+ :root[data-theme=dark] .lexical-root :global(.token.punctuation) {
235
+ color: #ccc;
236
+ }
237
+ :root[data-theme=dark] .lexical-root :global(.token.tag),
238
+ :root[data-theme=dark] .lexical-root :global(.token.attr-name),
239
+ :root[data-theme=dark] .lexical-root :global(.token.namespace),
240
+ :root[data-theme=dark] .lexical-root :global(.token.deleted) {
241
+ color: #e2777a;
242
+ }
243
+ :root[data-theme=dark] .lexical-root :global(.token.function-name) {
244
+ color: #6196cc;
245
+ }
246
+ :root[data-theme=dark] .lexical-root :global(.token.boolean),
247
+ :root[data-theme=dark] .lexical-root :global(.token.number),
248
+ :root[data-theme=dark] .lexical-root :global(.token.function) {
249
+ color: #f08d49;
250
+ }
251
+ :root[data-theme=dark] .lexical-root :global(.token.property),
252
+ :root[data-theme=dark] .lexical-root :global(.token.class-name),
253
+ :root[data-theme=dark] .lexical-root :global(.token.constant),
254
+ :root[data-theme=dark] .lexical-root :global(.token.symbol) {
255
+ color: #f8c555;
256
+ }
257
+ :root[data-theme=dark] .lexical-root :global(.token.selector),
258
+ :root[data-theme=dark] .lexical-root :global(.token.important),
259
+ :root[data-theme=dark] .lexical-root :global(.token.atrule),
260
+ :root[data-theme=dark] .lexical-root :global(.token.keyword),
261
+ :root[data-theme=dark] .lexical-root :global(.token.builtin) {
262
+ color: #cc99cd;
263
+ }
264
+ :root[data-theme=dark] .lexical-root :global(.token.string),
265
+ :root[data-theme=dark] .lexical-root :global(.token.char),
266
+ :root[data-theme=dark] .lexical-root :global(.token.attr-value),
267
+ :root[data-theme=dark] .lexical-root :global(.token.regex),
268
+ :root[data-theme=dark] .lexical-root :global(.token.variable) {
269
+ color: #7ec699;
270
+ }
271
+ :root[data-theme=dark] .lexical-root :global(.token.operator),
272
+ :root[data-theme=dark] .lexical-root :global(.token.entity),
273
+ :root[data-theme=dark] .lexical-root :global(.token.url) {
274
+ color: #67cdcc;
275
+ }
276
+ :root[data-theme=dark] .lexical-root :global(.token.important),
277
+ :root[data-theme=dark] .lexical-root :global(.token.bold) {
278
+ font-weight: bold;
279
+ }
280
+ :root[data-theme=dark] .lexical-root :global(.token.italic) {
281
+ font-style: italic;
282
+ }
283
+ :root[data-theme=dark] .lexical-root :global(.token.entity) {
284
+ cursor: help;
285
+ }
286
+ :root[data-theme=dark] .lexical-root :global(.token.inserted) {
287
+ color: green;
288
+ }</style>
@@ -17,11 +17,12 @@
17
17
 
18
18
  /**
19
19
  * @typedef {object} Props
20
- * @property {string | undefined} [value] - Input value.
20
+ * @property {string} [value] - Input value.
21
21
  * @property {boolean} [flex] - Make the text input container flexible.
22
22
  * @property {import('../../typedefs').TextEditorMode[]} [modes] - Enabled modes.
23
23
  * @property {(import('../../typedefs').TextEditorBlockType |
24
24
  * import('../../typedefs').TextEditorInlineType)[]} [buttons] - Enabled buttons.
25
+ * @property {import('../../typedefs').TextEditorComponent[]} [components] - Editor components.
25
26
  * @property {string} [class] - The `class` attribute on the wrapper element.
26
27
  * @property {boolean} [hidden] - Whether to hide the widget.
27
28
  * @property {boolean} [disabled] - Whether to disable the widget. An alias of the `aria-disabled`
@@ -44,6 +45,7 @@
44
45
  flex = false,
45
46
  modes = ['rich-text', 'plain-text'],
46
47
  buttons = [...inlineButtonTypes, ...blockButtonTypes],
48
+ components = [],
47
49
  hidden = false,
48
50
  disabled = false,
49
51
  readonly = false,
@@ -69,27 +71,33 @@
69
71
  * restore the original value when there is an error while conversion.
70
72
  */
71
73
  const convertMarkdown = async () => {
72
- if (!$editor) {
74
+ if (!$editor?.getRootElement()) {
73
75
  return;
74
76
  }
75
77
 
76
78
  const originalValue = inputValue;
77
79
 
78
80
  try {
79
- // We should avoid an empty editor; there should be at least one `<p>`, so give it an empty
80
- // string if the `value` is `undefined`
81
- // @see https://github.com/facebook/lexical/issues/2308
82
81
  await convertMarkdownToLexical($editor, inputValue ?? '');
83
- } catch {
82
+ } catch (ex) {
84
83
  $hasConverterError = true;
85
84
  inputValue = originalValue;
85
+ // eslint-disable-next-line no-console
86
+ console.error(ex);
86
87
  }
87
88
  };
88
89
 
89
90
  $effect(() => {
90
- if (!$editor) {
91
- return;
91
+ // We should avoid an empty editor; there should be at least one `<p>`, so give it an empty
92
+ // string if the `value` is `undefined`
93
+ // @see https://github.com/facebook/lexical/issues/2308
94
+ if ($editor?.getEditorState().isEmpty()) {
95
+ convertMarkdownToLexical($editor, '');
92
96
  }
97
+ });
98
+
99
+ $effect(() => {
100
+ void $editor;
93
101
 
94
102
  const newValue = value;
95
103
 
@@ -129,7 +137,7 @@
129
137
 
130
138
  // The editor has to be initialized in the browser
131
139
  onMount(() => {
132
- $editor = initEditor();
140
+ $editor = initEditor({ components });
133
141
  });
134
142
 
135
143
  setContext(
@@ -143,6 +151,7 @@
143
151
  useRichText,
144
152
  hasConverterError,
145
153
  enabledButtons: buttons,
154
+ components,
146
155
  convertMarkdown,
147
156
  }),
148
157
  );
@@ -21,6 +21,10 @@ declare const TextEditor: import("svelte").Component<{
21
21
  * - Enabled buttons.
22
22
  */
23
23
  buttons?: (import("../../typedefs").TextEditorBlockType | import("../../typedefs").TextEditorInlineType)[] | undefined;
24
+ /**
25
+ * - Editor components.
26
+ */
27
+ components?: import("../../typedefs").TextEditorComponent[] | undefined;
24
28
  /**
25
29
  * - The `class` attribute on the wrapper element.
26
30
  */
@@ -71,6 +75,10 @@ type Props = {
71
75
  * - Enabled buttons.
72
76
  */
73
77
  buttons?: (import("../../typedefs").TextEditorBlockType | import("../../typedefs").TextEditorInlineType)[] | undefined;
78
+ /**
79
+ * - Editor components.
80
+ */
81
+ components?: import("../../typedefs").TextEditorComponent[] | undefined;
74
82
  /**
75
83
  * - The `class` attribute on the wrapper element.
76
84
  */
@@ -12,7 +12,9 @@
12
12
  import Menu from '../../menu/menu.svelte';
13
13
  import Toolbar from '../../toolbar/toolbar.svelte';
14
14
  import FormatTextButton from './format-text-button.svelte';
15
+ import InsertImageButton from './insert-image-button.svelte';
15
16
  import InsertLinkButton from './insert-link-button.svelte';
17
+ import InsertMenuButton from './insert-menu-button.svelte';
16
18
  import ToggleBlockMenuItem from './toggle-block-menu-item.svelte';
17
19
 
18
20
  /**
@@ -45,9 +47,13 @@
45
47
  useRichText,
46
48
  hasConverterError,
47
49
  enabledButtons,
50
+ components,
48
51
  convertMarkdown,
49
52
  } = getContext('state');
50
53
 
54
+ const imageComponent = $derived(components.find(({ id }) => id === 'image'));
55
+ const otherComponents = $derived(components.filter(({ id }) => id !== 'image'));
56
+
51
57
  /**
52
58
  * Enabled block level buttons.
53
59
  * @type {import('../../../typedefs').TextEditorBlockType[]}
@@ -100,6 +106,15 @@
100
106
  {/each}
101
107
  </ButtonGroup>
102
108
  {/if}
109
+ {#if components.length}
110
+ <Divider orientation="vertical" />
111
+ {#if imageComponent}
112
+ <InsertImageButton component={imageComponent} />
113
+ {/if}
114
+ {#if otherComponents.length}
115
+ <InsertMenuButton components={otherComponents} />
116
+ {/if}
117
+ {/if}
103
118
  <Spacer flex />
104
119
  {#if modes.length > 1}
105
120
  <Button
@@ -0,0 +1,43 @@
1
+ <script>
2
+ import {
3
+ $createParagraphNode as createParagraphNode,
4
+ $insertNodes as insertNodes,
5
+ } from 'lexical';
6
+ import { getContext } from 'svelte';
7
+ import Button from '../../button/button.svelte';
8
+ import Icon from '../../icon/icon.svelte';
9
+
10
+ /**
11
+ * @typedef {object} Props
12
+ * @property {import('../../../typedefs').TextEditorComponent} component - Image editor component.
13
+ */
14
+
15
+ /** @type {Props} */
16
+ let {
17
+ /* eslint-disable prefer-const */
18
+ component,
19
+ /* eslint-enable prefer-const */
20
+ } = $props();
21
+
22
+ /**
23
+ * Text editor state.
24
+ * @type {import('../../../typedefs').TextEditorState}
25
+ */
26
+ const { editor, editorId, useRichText } = getContext('state');
27
+ </script>
28
+
29
+ <Button
30
+ iconic
31
+ aria-label={component.label}
32
+ aria-controls="{$editorId}-lexical-root"
33
+ disabled={!$useRichText}
34
+ onclick={() => {
35
+ $editor.update(() => {
36
+ insertNodes([component.createNode(), createParagraphNode()]);
37
+ });
38
+ }}
39
+ >
40
+ {#snippet startIcon()}
41
+ <Icon name={component.icon} />
42
+ {/snippet}
43
+ </Button>
@@ -0,0 +1,17 @@
1
+ export default InsertImageButton;
2
+ type InsertImageButton = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<Props>): void;
5
+ };
6
+ declare const InsertImageButton: import("svelte").Component<{
7
+ /**
8
+ * - Image editor component.
9
+ */
10
+ component: import("../../../typedefs").TextEditorComponent;
11
+ }, {}, "">;
12
+ type Props = {
13
+ /**
14
+ * - Image editor component.
15
+ */
16
+ component: import("../../../typedefs").TextEditorComponent;
17
+ };
@@ -0,0 +1,53 @@
1
+ <script>
2
+ import { $insertNodes as insertNodes } from 'lexical';
3
+ import { getContext } from 'svelte';
4
+ import { _ } from 'svelte-i18n';
5
+ import Icon from '../../icon/icon.svelte';
6
+ import MenuButton from '../../menu/menu-button.svelte';
7
+ import MenuItem from '../../menu/menu-item.svelte';
8
+ import Menu from '../../menu/menu.svelte';
9
+
10
+ /**
11
+ * @typedef {object} Props
12
+ * @property {import('../../../typedefs').TextEditorComponent[]} components - Editor components.
13
+ */
14
+
15
+ /** @type {Props} */
16
+ let {
17
+ /* eslint-disable prefer-const */
18
+ components,
19
+ /* eslint-enable prefer-const */
20
+ } = $props();
21
+
22
+ /**
23
+ * Text editor state.
24
+ * @type {import('../../../typedefs').TextEditorState}
25
+ */
26
+ const { editor, useRichText } = getContext('state');
27
+ </script>
28
+
29
+ <MenuButton disabled={!$useRichText} label={$_('_sui.insert')}>
30
+ {#snippet endIcon()}
31
+ <Icon name="arrow_drop_down" class="small-arrow" />
32
+ {/snippet}
33
+ {#snippet popup()}
34
+ <Menu>
35
+ {#each components as { id, label, icon, createNode } (id)}
36
+ <MenuItem
37
+ {label}
38
+ onclick={() => {
39
+ $editor.update(() => {
40
+ insertNodes([createNode()]);
41
+ });
42
+ }}
43
+ >
44
+ {#snippet startIcon()}
45
+ {#if icon}
46
+ <Icon name={icon} />
47
+ {/if}
48
+ {/snippet}
49
+ </MenuItem>
50
+ {/each}
51
+ </Menu>
52
+ {/snippet}
53
+ </MenuButton>
@@ -0,0 +1,17 @@
1
+ export default InsertMenuButton;
2
+ type InsertMenuButton = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<Props>): void;
5
+ };
6
+ declare const InsertMenuButton: import("svelte").Component<{
7
+ /**
8
+ * - Editor components.
9
+ */
10
+ components: import("../../../typedefs").TextEditorComponent[];
11
+ }, {}, "">;
12
+ type Props = {
13
+ /**
14
+ * - Editor components.
15
+ */
16
+ components: import("../../../typedefs").TextEditorComponent[];
17
+ };
@@ -1,4 +1,5 @@
1
1
  <script>
2
+ import { $createCodeNode as createCodeNode } from '@lexical/code';
2
3
  import { INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND } from '@lexical/list';
3
4
  import {
4
5
  $createHeadingNode as createHeadingNode,
@@ -78,6 +79,12 @@
78
79
  setBlocksType(getSelection(), () => createQuoteNode());
79
80
  });
80
81
  }
82
+
83
+ if (type === 'code-block') {
84
+ $editor.update(() => {
85
+ setBlocksType(getSelection(), () => createCodeNode());
86
+ });
87
+ }
81
88
  };
82
89
  </script>
83
90
 
@@ -13,8 +13,9 @@
13
13
 
14
14
  /**
15
15
  * @typedef {object} Props
16
- * @property {number | undefined} [min] - Minimum allowed value.
17
- * @property {number | undefined} [max] - Maximum allowed value.
16
+ * @property {number} [value] - Input value.
17
+ * @property {number} [min] - Minimum allowed value.
18
+ * @property {number} [max] - Maximum allowed value.
18
19
  * @property {number} [step] - Value to be added or removed when using the up/down arrow key or
19
20
  * button.
20
21
  * @property {import('svelte').Snippet} [increaseIcon] - Increase icon slot content.
@@ -48,41 +49,50 @@
48
49
 
49
50
  const id = generateElementId('input');
50
51
  let edited = $state(false);
52
+ let inputValue = $state('');
51
53
 
52
54
  const maximumFractionDigits = $derived(String(step).split('.')[1]?.length || 0);
53
- const isMin = $derived(typeof min === 'number' && Number(value || 0) <= min);
54
- const isMax = $derived(typeof max === 'number' && Number(value || 0) >= max);
55
+ const isMin = $derived(typeof min === 'number' && Number(inputValue || 0) <= min);
56
+ const isMax = $derived(typeof max === 'number' && Number(inputValue || 0) >= max);
55
57
 
56
58
  $effect(() => {
57
- invalid =
58
- (required && edited && (value === undefined || value === '')) ||
59
- (value !== undefined &&
60
- value !== '' &&
61
- (Number.isNaN(Number(value)) ||
62
- (typeof min === 'number' && Number(value || 0) < min) ||
63
- (typeof max === 'number' && Number(value || 0) > max)));
59
+ const newValue = inputValue.trim() ? Number(inputValue) : NaN;
60
+
61
+ value = !Number.isNaN(newValue) ? newValue : undefined;
62
+ });
63
+
64
+ $effect(() => {
65
+ if (edited) {
66
+ invalid =
67
+ (required && (value === undefined || inputValue === '')) ||
68
+ (inputValue !== undefined &&
69
+ inputValue !== '' &&
70
+ (Number.isNaN(Number(inputValue)) ||
71
+ (typeof min === 'number' && Number(inputValue || 0) < min) ||
72
+ (typeof max === 'number' && Number(inputValue || 0) > max)));
73
+ }
64
74
  });
65
75
 
66
76
  /**
67
77
  * Decrease the number.
68
78
  */
69
79
  const decrease = () => {
70
- if (isMin) {
80
+ if (isMin || Number.isNaN(Number(inputValue))) {
71
81
  return;
72
82
  }
73
83
 
74
- value = Number(Number(value || 0) - step).toFixed(maximumFractionDigits);
84
+ inputValue = Number(Number(inputValue || 0) - step).toFixed(maximumFractionDigits);
75
85
  };
76
86
 
77
87
  /**
78
88
  * Increase the number.
79
89
  */
80
90
  const increase = () => {
81
- if (isMax) {
91
+ if (isMax || Number.isNaN(Number(inputValue))) {
82
92
  return;
83
93
  }
84
94
 
85
- value = Number(Number(value || 0) + step).toFixed(maximumFractionDigits);
95
+ inputValue = Number(Number(inputValue || 0) + step).toFixed(maximumFractionDigits);
86
96
  };
87
97
  </script>
88
98
 
@@ -134,7 +144,7 @@
134
144
  {...restProps}
135
145
  role="spinbutton"
136
146
  {id}
137
- bind:value
147
+ bind:value={inputValue}
138
148
  spellcheck="false"
139
149
  {flex}
140
150
  {hidden}
@@ -164,6 +174,11 @@
164
174
  edited = true;
165
175
  }
166
176
  }}
177
+ oninput={() => {
178
+ if (!edited) {
179
+ edited = true;
180
+ }
181
+ }}
167
182
  {onChange}
168
183
  />
169
184
  </div>