@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
@@ -45,7 +45,7 @@
45
45
  <button
46
46
  bind:this={element}
47
47
  {...restProps}
48
- class="sui button {variant ?? ''} {size} {className}"
48
+ class="sui button {variant} {size} {className}"
49
49
  class:iconic
50
50
  class:pill
51
51
  class:flex
@@ -82,6 +82,9 @@
82
82
  border-top-right-radius: 4px !important;
83
83
  border-bottom-right-radius: 4px !important;
84
84
  }
85
+ .select-button-group[aria-invalid=true] :global(button) {
86
+ border-color: var(--sui-error-border-color);
87
+ }
85
88
  .select-button-group :global(button[aria-checked="true"]) {
86
89
  color: var(--sui-highlight-foreground-color);
87
90
  background-color: var(--sui-selected-background-color);
@@ -11,7 +11,7 @@
11
11
 
12
12
  /**
13
13
  * @typedef {object} Props
14
- * @property {string | undefined} [value] - Date.
14
+ * @property {string} [value] - Date.
15
15
  */
16
16
 
17
17
  /**
@@ -19,7 +19,7 @@
19
19
  * `aria-invalid` attribute.
20
20
  * @property {boolean | 'mixed'} [checked] - Whether to check the widget. An alias of the
21
21
  * `aria-checked` attribute.
22
- * @property {string | undefined} [label] - Text label displayed next to the checkbox.
22
+ * @property {string} [label] - Text label displayed next to the checkbox.
23
23
  * @property {string} [aria-label] - `aria-label` attribute.
24
24
  * @property {import('svelte').Snippet} [checkIcon] - Check icon slot content.
25
25
  */
@@ -88,7 +88,7 @@
88
88
  {disabled}
89
89
  {readonly}
90
90
  {required}
91
- {invalid}
91
+ aria-invalid={invalid}
92
92
  aria-checked={checked}
93
93
  aria-label={ariaLabel || undefined}
94
94
  aria-labelledby={ariaLabel ? undefined : `${id}-label`}
@@ -174,6 +174,13 @@
174
174
  .checkbox :global(button[aria-checked="false"]) {
175
175
  color: transparent;
176
176
  }
177
+ .checkbox :global(button[aria-invalid="true"]) {
178
+ border-color: var(--sui-error-border-color);
179
+ color: var(--sui-error-foreground-color);
180
+ }
181
+ .checkbox :global(button[aria-checked="true"][aria-invalid="true"]) {
182
+ background-color: var(--sui-error-background-color);
183
+ }
177
184
  .checkbox label {
178
185
  cursor: inherit;
179
186
  }
@@ -13,7 +13,7 @@
13
13
  * @property {boolean} [selected] - Whether to allow selecting more than one `<GridRow>` and/or
14
14
  * `<GridCell>`. An alias of the `aria-multiselectable` attribute.
15
15
  * @property {boolean} [clickToSelect] - Whether to select a row by clicking on it.
16
- * @property {HTMLElement | undefined} [element] - A reference to the wrapper element.
16
+ * @property {HTMLElement} [element] - A reference to the wrapper element.
17
17
  * @property {import('svelte').Snippet} [children] - Primary slot content.
18
18
  * @property {(event: CustomEvent) => void} [onChange] - Custom `Change` event handler.
19
19
  */
@@ -102,6 +102,9 @@
102
102
  margin: 4px;
103
103
  background-color: var(--sui-control-border-color);
104
104
  }
105
+ [role=listbox][aria-invalid=true] {
106
+ border-color: var(--sui-error-border-color);
107
+ }
105
108
  [role=listbox]:global(.tabs) {
106
109
  padding: 0;
107
110
  border-width: 0 1px 0 0;
@@ -10,7 +10,7 @@
10
10
 
11
11
  /**
12
12
  * @typedef {object} Props
13
- * @property {HTMLElement | undefined} [popupPositionBaseElement] - The base element of
13
+ * @property {HTMLElement} [popupPositionBaseElement] - The base element of
14
14
  * {@link popupPosition}. If omitted, this will be {@link buttonComponent}.
15
15
  */
16
16
 
@@ -42,9 +42,12 @@
42
42
 
43
43
  <style>.progressbar {
44
44
  overflow: hidden;
45
+ border-width: var(--sui-progressbar-border-width, 1px);
46
+ border-style: var(--sui-progressbar-border-style, solid);
47
+ border-color: var(--sui-progressbar-border-color, var(--sui-control-border-color));
45
48
  border-radius: var(--sui-progressbar-border-radius, 16px);
46
49
  width: var(--sui-progressbar-width, 240px);
47
- height: var(--sui-progressbar-height, 8px);
50
+ height: var(--sui-progressbar-height, 10px);
48
51
  background-color: var(--sui-progressbar-background-color, var(--sui-secondary-background-color));
49
52
  }
50
53
  .progressbar div {
@@ -86,6 +86,15 @@
86
86
  gap: 8px;
87
87
  }
88
88
  }
89
+ .radio-group[aria-invalid=true] :global(button) {
90
+ border-color: var(--sui-error-border-color);
91
+ }
92
+ .radio-group[aria-invalid=true] :global(button[aria-checked="true"]) {
93
+ border-color: var(--sui-error-border-color);
94
+ }
95
+ .radio-group[aria-invalid=true] :global(button[aria-checked="true"]::before) {
96
+ background-color: var(--sui-error-border-color);
97
+ }
89
98
 
90
99
  .inner {
91
100
  display: contents;
@@ -17,9 +17,9 @@
17
17
  * attribute.
18
18
  * @property {boolean} [checked] - Whether to check the widget. An alias of the `aria-checked`
19
19
  * attribute.
20
- * @property {string | undefined} [name] - The `name` attribute on the `<button>` element.
21
- * @property {string | undefined} [value] - The `value` attribute on the `<button>` element.
22
- * @property {string | undefined} [label] - Text label displayed next to the checkbox.
20
+ * @property {string} [name] - The `name` attribute on the `<button>` element.
21
+ * @property {string} [value] - The `value` attribute on the `<button>` element.
22
+ * @property {string} [label] - Text label displayed next to the checkbox.
23
23
  * @property {import('svelte').Snippet} [children] - Primary slot content.
24
24
  * @property {import('svelte').Snippet} [default] - Default slot content.
25
25
  * @property {(event: CustomEvent) => void} [onChange] - Custom `Change` event handler.
@@ -122,8 +122,9 @@
122
122
  aria-hidden={hidden}
123
123
  aria-disabled={disabled}
124
124
  aria-readonly={readonly}
125
+ aria-required={required}
126
+ aria-invalid={invalid}
125
127
  aria-haspopup="listbox"
126
- aria-activedescendant="selected-option"
127
128
  >
128
129
  <div role="none" class="label">
129
130
  {value !== undefined ? label : $_('_sui.combobox.select_an_option')}
@@ -144,7 +145,6 @@
144
145
  aria-controls="{id}-popup"
145
146
  aria-expanded={isPopupOpen}
146
147
  aria-haspopup="listbox"
147
- aria-activedescendant="selected-option"
148
148
  />
149
149
  {/if}
150
150
  <Button
@@ -282,6 +282,10 @@
282
282
  .combobox div[role=combobox]:hover, .combobox div[role=combobox]:focus {
283
283
  background-color: var(--sui-hover-background-color);
284
284
  }
285
+ .combobox div[role=combobox][aria-invalid=true] {
286
+ border-color: var(--sui-error-border-color);
287
+ background-color: var(--sui-error-background-color);
288
+ }
285
289
  .combobox div[role=combobox] .label {
286
290
  display: block;
287
291
  overflow: hidden;
@@ -10,7 +10,7 @@
10
10
  * @property {{ label: string, value: string, searchValue?: string }[]} options - Available
11
11
  * options.
12
12
  * @property {string[]} [values] - Selected option values.
13
- * @property {number | undefined} [max] - Maximum number of selectable options.
13
+ * @property {number} [max] - Maximum number of selectable options.
14
14
  * @property {string} [class] - The `class` attribute on the wrapper element.
15
15
  * @property {boolean} [hidden] - Whether to hide the widget.
16
16
  * @property {boolean} [disabled] - Whether to disable the widget. An alias of the `aria-disabled`
@@ -16,8 +16,8 @@
16
16
  * @property {number} [min] - Minimum allowed value. An alias of the `aria-valuemin` attribute.
17
17
  * @property {number} [max] - Maximum allowed value. An alias of the `aria-valuemax` attribute.
18
18
  * @property {string} [sliderLabel] - `aria-label` on the slider.
19
- * @property {[number, number] | undefined} [values] - Value list for a multi-thumb slider.
20
- * @property {[string, string] | undefined} [sliderLabels] - `aria-label` on a multi-thumb slider.
19
+ * @property {[number, number]} [values] - Value list for a multi-thumb slider.
20
+ * @property {[string, string]} [sliderLabels] - `aria-label` on a multi-thumb slider.
21
21
  * @property {number} [step] - Step option like `<input type="range">`.
22
22
  * @property {(string[] | number[])} [optionLabels] - Visible labels on the slider.
23
23
  * @property {boolean} [flex] - Make the text input container flexible.
@@ -304,6 +304,7 @@
304
304
  class="sui slider {className}"
305
305
  class:disabled
306
306
  class:readonly
307
+ class:invalid
307
308
  {hidden}
308
309
  >
309
310
  <div bind:this={base} role="none" class="base" onpointerdown={(event) => onPointerDown(event)}>
@@ -316,7 +317,7 @@
316
317
  <div
317
318
  role="slider"
318
319
  tabindex={disabled ? -1 : 0}
319
- aria-label={multiThumb ? sliderLabels?.[0] || '' : sliderLabel}
320
+ aria-label={multiThumb ? sliderLabels?.[0] : sliderLabel}
320
321
  aria-hidden={hidden}
321
322
  aria-disabled={disabled}
322
323
  aria-readonly={readonly}
@@ -332,7 +333,7 @@
332
333
  <div
333
334
  role="slider"
334
335
  tabindex={disabled ? -1 : 0}
335
- aria-label={sliderLabels?.[1] || ''}
336
+ aria-label={sliderLabels?.[1]}
336
337
  aria-hidden={hidden}
337
338
  aria-disabled={disabled}
338
339
  aria-readonly={readonly}
@@ -363,7 +364,7 @@
363
364
  position: relative;
364
365
  display: inline-block;
365
366
  margin: var(--sui-focus-ring-width);
366
- padding: var(--sui-checkbox-height) calc(var(--sui-checkbox-height) / 2);
367
+ padding: 4px 6px;
367
368
  touch-action: none;
368
369
  }
369
370
  .slider:hover .base-bar {
@@ -375,7 +376,7 @@
375
376
 
376
377
  .base {
377
378
  position: relative;
378
- width: var(--sui-slider-base-width, calc(var(--sui-checkbox-height) * 10));
379
+ width: var(--sui-slider-base-width, 240px);
379
380
  height: calc(var(--sui-checkbox-height) / 2);
380
381
  cursor: pointer;
381
382
  }
@@ -385,7 +386,7 @@
385
386
  border-style: solid;
386
387
  border-color: var(--sui-control-border-color);
387
388
  border-radius: var(--sui-checkbox-height);
388
- background-color: var(--sui-button-background-color);
389
+ background-color: var(--sui-slider-background-color, var(--sui-secondary-background-color));
389
390
  transition: all 200ms;
390
391
  width: 100%;
391
392
  height: 100%;
@@ -398,6 +399,9 @@
398
399
  border-radius: var(--sui-checkbox-height);
399
400
  background-color: var(--sui-primary-accent-color-light);
400
401
  }
402
+ .invalid .slider-bar {
403
+ background-color: var(--sui-error-border-color);
404
+ }
401
405
 
402
406
  [role=slider] {
403
407
  position: absolute;
@@ -410,6 +414,9 @@
410
414
  cursor: pointer;
411
415
  transform: translate(calc((var(--sui-checkbox-height) / 2 - 1px) * -1), calc((var(--sui-checkbox-height) / 4 - 1px) * -1));
412
416
  }
417
+ .invalid [role=slider] {
418
+ border-color: var(--sui-error-border-color);
419
+ }
413
420
 
414
421
  .label {
415
422
  position: absolute;
@@ -7,7 +7,7 @@
7
7
  <script>
8
8
  /**
9
9
  * @typedef {object} Props
10
- * @property {string | undefined} [label] - Text label displayed next to the switch.
10
+ * @property {string} [label] - Text label displayed next to the switch.
11
11
  * @property {string} [class] - The `class` attribute on the wrapper element.
12
12
  * @property {boolean} [hidden] - Whether to hide the widget.
13
13
  * @property {boolean} [disabled] - Whether to disable the widget. An alias of the `aria-disabled`
@@ -90,6 +90,9 @@
90
90
  -webkit-user-select: none;
91
91
  user-select: none;
92
92
  }
93
+ button[aria-invalid=true] span {
94
+ background-color: var(--sui-error-border-color) !important;
95
+ }
93
96
  button:hover[aria-checked=false] span {
94
97
  background-color: var(--sui-hover-background-color);
95
98
  }
@@ -16,7 +16,7 @@
16
16
  * attribute.
17
17
  * @property {'horizontal' | 'vertical'} [orientation] - Orientation of the widget. An alias of
18
18
  * the `aria-orientation` attribute.
19
- * @property {string | undefined} [name] - The `data-name` attribute on the wrapper element.
19
+ * @property {string} [name] - The `data-name` attribute on the wrapper element.
20
20
  * @property {import('svelte').Snippet} [children] - Primary slot content.
21
21
  * @property {(event: CustomEvent) => void} [onChange] - Custom `Change` event handler.
22
22
  */
@@ -1,3 +1,5 @@
1
- export function initEditor(): import("lexical").LexicalEditor;
1
+ export function initEditor({ components }?: {
2
+ components?: import("../../typedefs").TextEditorComponent[] | undefined;
3
+ } | undefined): import("lexical").LexicalEditor;
2
4
  export function convertMarkdown(editor: import("lexical").LexicalEditor, value: string): Promise<void>;
3
5
  export function focusEditor(editor: import("lexical").LexicalEditor): Promise<void>;
@@ -1,4 +1,10 @@
1
- import { CodeHighlightNode, CodeNode } from '@lexical/code';
1
+ import {
2
+ CodeHighlightNode,
3
+ CodeNode,
4
+ $isCodeHighlightNode as isCodeHighlightNode,
5
+ $isCodeNode as isCodeNode,
6
+ registerCodeHighlighting,
7
+ } from '@lexical/code';
2
8
  import { registerDragonSupport } from '@lexical/dragon';
3
9
  import { createEmptyHistoryState, registerHistory } from '@lexical/history';
4
10
  import {
@@ -31,6 +37,7 @@ import {
31
37
  } from '@lexical/rich-text';
32
38
  import { TableCellNode, TableNode, TableRowNode } from '@lexical/table';
33
39
  import { $getNearestNodeOfType as getNearestNodeOfType } from '@lexical/utils';
40
+ import { sleep } from '@sveltia/utils/misc';
34
41
  import {
35
42
  COMMAND_PRIORITY_NORMAL,
36
43
  ElementNode,
@@ -41,8 +48,11 @@ import {
41
48
  $getSelection as getSelection,
42
49
  $isRangeSelection as isRangeSelection,
43
50
  } from 'lexical';
51
+ import prismComponents from 'prismjs/components';
44
52
  import { blockButtonTypes, textFormatButtonTypes } from '.';
45
53
 
54
+ const allTransformers = [...TRANSFORMERS];
55
+
46
56
  /**
47
57
  * Lexical editor configuration.
48
58
  * @type {import('lexical').CreateEditorArgs}
@@ -74,31 +84,66 @@ const editorConfig = {
74
84
  listitem: 'nested',
75
85
  },
76
86
  },
87
+ code: 'code-block',
88
+ // https://github.com/facebook/lexical/blob/main/packages/lexical-website/docs/getting-started/theming.md
89
+ codeHighlight: {
90
+ atrule: 'token atrule',
91
+ attr: 'token attr',
92
+ boolean: 'token boolean',
93
+ builtin: 'token builtin',
94
+ cdata: 'token cdata',
95
+ char: 'token char',
96
+ class: 'token class',
97
+ 'class-name': 'token class-name',
98
+ comment: 'token comment',
99
+ constant: 'token constant',
100
+ deleted: 'token deleted',
101
+ doctype: 'token doctype',
102
+ entity: 'token entity',
103
+ function: 'token function',
104
+ important: 'token important',
105
+ inserted: 'token inserted',
106
+ keyword: 'token keyword',
107
+ namespace: 'token namespace',
108
+ number: 'token number',
109
+ operator: 'token operator',
110
+ prolog: 'token prolog',
111
+ property: 'token property',
112
+ punctuation: 'token punctuation',
113
+ regex: 'token regex',
114
+ selector: 'token selector',
115
+ string: 'token string',
116
+ symbol: 'token symbol',
117
+ tag: 'token tag',
118
+ url: 'token url',
119
+ variable: 'token variable',
120
+ },
77
121
  },
78
122
  };
79
123
 
80
124
  /**
81
- * Listen to changes on the editor.
82
- * @param {import('lexical').LexicalEditor} editor - Editor instance.
125
+ * Get the current selection’s block and inline level types.
126
+ * @returns {{ blockType: import('../../typedefs').TextEditorBlockType,
127
+ * inlineTypes: import('../../typedefs').TextEditorInlineType[] }} Types.
83
128
  */
84
- const onEditorUpdate = (editor) => {
129
+ const getSelectionTypes = () => {
85
130
  const selection = getSelection();
86
131
 
87
132
  if (!isRangeSelection(selection)) {
88
- return;
133
+ return { blockType: 'paragraph', inlineTypes: [] };
89
134
  }
90
135
 
91
136
  const anchor = selection.anchor.getNode();
92
137
  /** @type {ElementNode | null} */
93
138
  let parent = null;
94
139
  /** @type {import('../../typedefs').TextEditorInlineType[]} */
95
- const selectionInlineTypes = textFormatButtonTypes.filter((type) => selection.hasFormat(type));
140
+ const inlineTypes = textFormatButtonTypes.filter((type) => selection.hasFormat(type));
96
141
 
97
142
  if (anchor.getType() !== 'root') {
98
143
  parent = anchor instanceof ElementNode ? anchor : getNearestNodeOfType(anchor, ElementNode);
99
144
 
100
145
  if (isLinkNode(parent)) {
101
- selectionInlineTypes.push('link');
146
+ inlineTypes.push('link');
102
147
  parent = getNearestNodeOfType(parent, ElementNode);
103
148
  }
104
149
 
@@ -107,7 +152,7 @@ const onEditorUpdate = (editor) => {
107
152
  }
108
153
  }
109
154
 
110
- const selectionBlockType = /** @type {import('../../typedefs').TextEditorBlockType} */ (
155
+ const blockType = /** @type {import('../../typedefs').TextEditorBlockType} */ (
111
156
  (() => {
112
157
  if (!parent) {
113
158
  return 'paragraph';
@@ -125,6 +170,10 @@ const onEditorUpdate = (editor) => {
125
170
  return 'blockquote';
126
171
  }
127
172
 
173
+ if (isCodeNode(parent) || isCodeHighlightNode(parent)) {
174
+ return 'code-block';
175
+ }
176
+
128
177
  const type = parent.getType();
129
178
 
130
179
  if (blockButtonTypes.includes(/** @type {any} */ (type))) {
@@ -135,15 +184,25 @@ const onEditorUpdate = (editor) => {
135
184
  })()
136
185
  );
137
186
 
187
+ return { blockType, inlineTypes };
188
+ };
189
+
190
+ /**
191
+ * Listen to changes made on the editor and trigger the Update event.
192
+ * @param {import('lexical').LexicalEditor} editor - Editor instance.
193
+ */
194
+ const onEditorUpdate = (editor) => {
195
+ const { blockType, inlineTypes } = getSelectionTypes();
196
+
138
197
  editor.getRootElement()?.dispatchEvent(
139
198
  new CustomEvent('Update', {
140
199
  detail: {
141
200
  value: convertToMarkdownString(
142
201
  // Use underscores for italic text in Markdown instead of asterisks
143
- TRANSFORMERS.filter((/** @type {any} */ { tag }) => tag !== '*'),
202
+ allTransformers.filter((/** @type {any} */ { tag }) => tag !== '*'),
144
203
  ),
145
- selectionBlockType,
146
- selectionInlineTypes,
204
+ selectionBlockType: blockType,
205
+ selectionInlineTypes: inlineTypes,
147
206
  },
148
207
  }),
149
208
  );
@@ -151,15 +210,29 @@ const onEditorUpdate = (editor) => {
151
210
 
152
211
  /**
153
212
  * Initialize the Lexical editor.
213
+ * @param {object} [options] - Options.
214
+ * @param {import('../../typedefs').TextEditorComponent[]} [options.components] - Editor components.
154
215
  * @returns {import('lexical').LexicalEditor} Editor instance.
155
216
  */
156
- export const initEditor = () => {
217
+ export const initEditor = ({ components } = {}) => {
218
+ components?.forEach(({ node, transformer }) => {
219
+ /** @type {any[]} */ (editorConfig.nodes).unshift(node);
220
+ allTransformers.unshift(transformer);
221
+ });
222
+
157
223
  const editor = createEditor(editorConfig);
158
224
 
159
225
  registerRichText(editor);
160
226
  registerDragonSupport(editor);
161
227
  registerHistory(editor, createEmptyHistoryState(), 1000);
162
228
 
229
+ registerCodeHighlighting(editor, {
230
+ defaultLanguage: 'plain',
231
+ // eslint-disable-next-line jsdoc/require-jsdoc
232
+ tokenize: (code, language = 'plain') =>
233
+ window.Prism.tokenize(code, window.Prism.languages[language] ?? window.Prism.languages.plain),
234
+ });
235
+
163
236
  editor.registerCommand(
164
237
  TOGGLE_LINK_COMMAND,
165
238
  (payload) => {
@@ -250,26 +323,47 @@ export const initEditor = () => {
250
323
  * @returns {Promise<void>} Nothing.
251
324
  * @throws {Error} Failed to convert the value to Lexical nodes.
252
325
  */
253
- export const convertMarkdown = async (editor, value) =>
254
- new Promise((resolve, reject) => {
326
+ export const convertMarkdown = async (editor, value) => {
327
+ // Load Prism language support on demand; the `loadLanguages` Prism utility method cannot be used
328
+ await Promise.all(
329
+ [...value.matchAll(/^```(?<lang>.+?)\n/gm)].map(async ({ groups: { lang } = {} }) => {
330
+ if (!(lang in window.Prism.languages) && lang in prismComponents.languages) {
331
+ try {
332
+ await import(
333
+ // eslint-disable-next-line jsdoc/no-bad-blocks
334
+ /* @vite-ignore */ `https://unpkg.com/prismjs@1.29.0/components/prism-${lang}.min.js`
335
+ );
336
+ } catch {
337
+ //
338
+ }
339
+ }
340
+ }),
341
+ );
342
+
343
+ return new Promise((resolve, reject) => {
255
344
  editor.update(() => {
256
345
  try {
257
- convertFromMarkdownString(value, TRANSFORMERS);
346
+ convertFromMarkdownString(value, allTransformers);
258
347
  resolve(void 0);
259
- } catch {
260
- reject(new Error('Failed to convert Markdown'));
348
+ } catch (ex) {
349
+ reject(new Error('Failed to convert Markdown', { cause: ex }));
261
350
  }
262
351
  });
263
352
  });
353
+ };
264
354
 
265
355
  /**
266
356
  * Move focus to the editor so the user can start editing immediately.
267
357
  * @param {import('lexical').LexicalEditor} editor - Editor instance.
268
358
  * @returns {Promise<void>} Nothing.
269
359
  */
270
- export const focusEditor = async (editor) =>
271
- new Promise((resolve) => {
360
+ export const focusEditor = async (editor) => {
361
+ await sleep(100);
362
+ editor.getRootElement()?.focus();
363
+
364
+ return new Promise((resolve) => {
272
365
  editor.focus(() => {
273
366
  resolve(undefined);
274
367
  });
275
368
  });
369
+ };
@@ -73,6 +73,11 @@ export const availableButtons = {
73
73
  icon: 'format_quote',
74
74
  inline: false,
75
75
  },
76
+ 'code-block': {
77
+ labelKey: 'code_block',
78
+ icon: 'code_blocks',
79
+ inline: false,
80
+ },
76
81
  };
77
82
  /**
78
83
  * @type {import('../../typedefs').TextEditorFormatType[]}
@@ -96,4 +101,5 @@ export const blockButtonTypes = [
96
101
  'bulleted-list',
97
102
  'numbered-list',
98
103
  'blockquote',
104
+ 'code-block',
99
105
  ];