@sveltia/ui 0.12.2 → 0.12.4

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.
@@ -96,11 +96,11 @@
96
96
  .split-button :global(button) {
97
97
  margin: 0;
98
98
  }
99
- .split-button :global(button):not(.menu-button) {
99
+ .split-button :global(button:not(.menu-button)) {
100
100
  border-top-right-radius: 0;
101
101
  border-bottom-right-radius: 0;
102
102
  }
103
- .split-button :global(button):is(.menu-button) {
103
+ .split-button :global(button.menu-button) {
104
104
  border-left-width: 0;
105
105
  border-top-left-radius: 0;
106
106
  border-bottom-left-radius: 0;
@@ -29,7 +29,7 @@
29
29
  aria-hidden={hidden}
30
30
  aria-orientation={orientation}
31
31
  {...$$restProps}
32
- />
32
+ ></div>
33
33
 
34
34
  <style>.divider {
35
35
  flex: none;
@@ -16,7 +16,7 @@
16
16
  export let flex = false;
17
17
  </script>
18
18
 
19
- <div role="none" class="sui spacer {className}" class:flex {...$$restProps} />
19
+ <div role="none" class="sui spacer {className}" class:flex {...$$restProps}></div>
20
20
 
21
21
  <style>.spacer.flex {
22
22
  flex: auto;
@@ -31,18 +31,26 @@
31
31
  {...$$restProps}
32
32
  >
33
33
  {#if label}
34
- <tr class="row-group-caption">
35
- <th id="{id}-label" colspan="9999" scope="rowgroup">{label}</th>
36
- </tr>
34
+ <div role="row" class="row-group-caption">
35
+ <!-- We need `colspan` here but cannot place `<th>` under `<div>`, so use a hack -->
36
+ <svelte:element this="th" role="rowheader" id="{id}-label" colspan="9999">
37
+ {label}
38
+ </svelte:element>
39
+ </div>
37
40
  {/if}
38
41
  <slot />
39
42
  </div>
40
43
 
41
- <style>.grid-body {
44
+ <style>[role=rowgroup] {
42
45
  display: table-row-group;
43
46
  }
44
47
 
45
- th {
48
+ [role=row] {
49
+ display: table-row;
50
+ }
51
+
52
+ [role=rowheader] {
53
+ display: table-cell;
46
54
  padding: 8px;
47
55
  color: var(--sui-secondary-foreground-color);
48
56
  background-color: var(--sui-secondary-background-color);
@@ -304,12 +304,12 @@
304
304
  {...$$restProps}
305
305
  >
306
306
  <div role="none" class="base" bind:this={base} on:pointerdown={(event) => onPointerDown(event)}>
307
- <div role="none" class="base-bar" />
307
+ <div role="none" class="base-bar"></div>
308
308
  <div
309
309
  class="slider-bar"
310
310
  style:left="{multiThumb ? sliderPositions[0] : 0}px"
311
311
  style:width="{multiThumb ? sliderPositions[1] - sliderPositions[0] : sliderPositions[0]}px"
312
- />
312
+ ></div>
313
313
  <div
314
314
  role="slider"
315
315
  tabindex={disabled ? -1 : 0}
@@ -324,7 +324,7 @@
324
324
  style:left="{sliderPositions[0]}px"
325
325
  on:pointerdown={(event) => onPointerDown(event, 0)}
326
326
  on:keydown={(event) => onKeyDown(event, 0)}
327
- />
327
+ ></div>
328
328
  {#if multiThumb}
329
329
  <div
330
330
  role="slider"
@@ -340,7 +340,7 @@
340
340
  style:left="{sliderPositions[1]}px"
341
341
  on:pointerdown={(event) => onPointerDown(event, 1)}
342
342
  on:keydown={(event) => onKeyDown(event, 1)}
343
- />
343
+ ></div>
344
344
  {/if}
345
345
  {#if optionLabels.length}
346
346
  {#each optionLabels as label, index}
@@ -72,7 +72,7 @@
72
72
  }
73
73
  }}
74
74
  >
75
- <span role="none" />
75
+ <span role="none"></span>
76
76
  {#if label}
77
77
  {label}
78
78
  {:else}
@@ -31,18 +31,26 @@
31
31
  {...$$restProps}
32
32
  >
33
33
  {#if label}
34
- <tr class="row-group-caption">
35
- <th id="{id}-label" colspan="9999" scope="rowgroup">{label}</th>
36
- </tr>
34
+ <div role="row" class="row-group-caption">
35
+ <!-- We need `colspan` here but cannot place `<th>` under `<div>`, so use a hack -->
36
+ <svelte:element this="th" role="rowheader" id="{id}-label" colspan="9999">
37
+ {label}
38
+ </svelte:element>
39
+ </div>
37
40
  {/if}
38
41
  <slot />
39
42
  </div>
40
43
 
41
- <style>.table-body {
44
+ <style>[role=rowgroup] {
42
45
  display: table-row-group;
43
46
  }
44
47
 
45
- th {
48
+ [role=row] {
49
+ display: table-row;
50
+ }
51
+
52
+ [role=rowheader] {
53
+ display: table-cell;
46
54
  padding: 8px;
47
55
  color: var(--sui-secondary-foreground-color);
48
56
  background-color: var(--sui-secondary-background-color);
@@ -108,7 +108,7 @@
108
108
  <div role="none" class="inner" inert={disabled}>
109
109
  <slot />
110
110
  </div>
111
- <div role="none" class="indicator" style={indicatorStyle} />
111
+ <div role="none" class="indicator" style={indicatorStyle}></div>
112
112
  </div>
113
113
 
114
114
  <style>.tab-list {
@@ -1,6 +1,11 @@
1
1
  <script>
2
2
  import { getContext, onMount } from 'svelte';
3
3
 
4
+ /**
5
+ * Whether to hide the widget. An alias of the `aria-hidden` attribute.
6
+ * @type {boolean | undefined}
7
+ */
8
+ export let hidden = undefined;
4
9
  /**
5
10
  * Whether to disable the widget. An alias of the `aria-disabled` attribute.
6
11
  * @type {boolean}
@@ -11,6 +16,16 @@
11
16
  * @type {boolean}
12
17
  */
13
18
  export let readonly = false;
19
+ /**
20
+ * Whether to mark the widget required. An alias of the `aria-required` attribute.
21
+ * @type {boolean}
22
+ */
23
+ export let required = false;
24
+ /**
25
+ * Whether to mark the widget invalid. An alias of the `aria-invalid` attribute.
26
+ * @type {boolean}
27
+ */
28
+ export let invalid = false;
14
29
  /**
15
30
  * Input value.
16
31
  * @type {string | undefined}
@@ -21,14 +36,8 @@
21
36
  * Text editor state.
22
37
  * @type {TextEditorState}
23
38
  */
24
- const {
25
- editor,
26
- editorId,
27
- selectionBlockType,
28
- selectionInlineTypes,
29
- useRichText,
30
- hasConverterError,
31
- } = getContext('state');
39
+ const { editor, editorId, selectionBlockType, selectionInlineTypes, hasConverterError } =
40
+ getContext('state');
32
41
 
33
42
  /**
34
43
  * Reference to the Lexical editor root element.
@@ -86,19 +95,22 @@
86
95
  <div
87
96
  role="textbox"
88
97
  aria-multiline="true"
98
+ aria-hidden={hidden}
89
99
  aria-disabled={disabled}
90
100
  aria-readonly={readonly}
101
+ aria-required={required}
102
+ aria-invalid={invalid}
91
103
  class="lexical-root"
92
104
  id="{$editorId}-lexical-root"
93
105
  contenteditable={editable}
106
+ {hidden}
94
107
  bind:this={lexicalRoot}
95
- hidden={!$useRichText}
96
- />
108
+ ></div>
97
109
 
98
110
  <style>.lexical-root {
99
111
  border: 1px solid var(--sui-textbox-border-color);
100
112
  border-radius: 0 0 var(--sui-textbox-border-radius) var(--sui-textbox-border-radius) !important;
101
- padding: 16px;
113
+ padding: var(--sui-textbox-multiline-padding);
102
114
  min-height: 8em;
103
115
  color: var(--sui-textbox-foreground-color);
104
116
  background-color: var(--sui-textbox-background-color);
@@ -109,6 +121,9 @@
109
121
  .lexical-root:focus-visible {
110
122
  outline: 0;
111
123
  }
124
+ .lexical-root[aria-invalid=true] {
125
+ border-color: var(--sui-error-foreground-color);
126
+ }
112
127
  .lexical-root > :global(:first-child) {
113
128
  margin-top: 0;
114
129
  }
@@ -2,9 +2,12 @@
2
2
  /** @typedef {typeof __propDef.events} LexicalRootEvents */
3
3
  /** @typedef {typeof __propDef.slots} LexicalRootSlots */
4
4
  export default class LexicalRoot extends SvelteComponent<{
5
+ invalid?: boolean | undefined;
5
6
  disabled?: boolean | undefined;
6
7
  value?: string | undefined;
8
+ hidden?: boolean | undefined;
7
9
  readonly?: boolean | undefined;
10
+ required?: boolean | undefined;
8
11
  }, {
9
12
  [evt: string]: CustomEvent<any>;
10
13
  }, {}> {
@@ -15,9 +18,12 @@ export type LexicalRootSlots = typeof __propDef.slots;
15
18
  import { SvelteComponent } from "svelte";
16
19
  declare const __propDef: {
17
20
  props: {
21
+ invalid?: boolean | undefined;
18
22
  disabled?: boolean | undefined;
19
23
  value?: string | undefined;
24
+ hidden?: boolean | undefined;
20
25
  readonly?: boolean | undefined;
26
+ required?: boolean | undefined;
21
27
  };
22
28
  events: {
23
29
  [evt: string]: CustomEvent<any>;
@@ -121,7 +121,14 @@
121
121
 
122
122
  <div role="none" class="sui text-editor" hidden={hidden || undefined} {...$$restProps}>
123
123
  <EditorToolbar {disabled} {readonly} />
124
- <LexicalRoot {disabled} {readonly} bind:value />
124
+ <LexicalRoot
125
+ bind:value
126
+ hidden={!$useRichText || hidden}
127
+ {disabled}
128
+ {readonly}
129
+ {required}
130
+ {invalid}
131
+ />
125
132
  <TextArea
126
133
  autoResize={true}
127
134
  bind:value
@@ -1,13 +1,13 @@
1
1
  <script>
2
- import { getContext, tick } from 'svelte';
2
+ import { getContext } from 'svelte';
3
3
  import { _ } from 'svelte-i18n';
4
4
  import ButtonGroup from '../../button/button-group.svelte';
5
+ import Button from '../../button/button.svelte';
5
6
  import Divider from '../../divider/divider.svelte';
6
7
  import Spacer from '../../divider/spacer.svelte';
7
8
  import Icon from '../../icon/icon.svelte';
8
9
  import MenuButton from '../../menu/menu-button.svelte';
9
10
  import Menu from '../../menu/menu.svelte';
10
- import Switch from '../../switch/switch.svelte';
11
11
  import {
12
12
  availableButtons,
13
13
  blockButtonTypes,
@@ -99,20 +99,21 @@
99
99
  {/if}
100
100
  <Spacer flex />
101
101
  {#if modes.length > 1}
102
- <Switch
102
+ <Button
103
+ iconic
103
104
  disabled={$hasConverterError}
104
- bind:checked={$useRichText}
105
- label={$_('_sui.text_editor.rich_text')}
106
- aria-label={$_('_sui.text_editor.use_rich_text_mode')}
107
- on:change={async () => {
108
- // Wait for `$useRichText` to be updated
109
- await tick();
105
+ pressed={!$useRichText}
106
+ aria-label={$_('_sui.text_editor.edit_in_markdown')}
107
+ on:click={() => {
108
+ $useRichText = !$useRichText;
110
109
 
111
110
  if ($useRichText) {
112
111
  convertMarkdown();
113
112
  }
114
113
  }}
115
- />
114
+ >
115
+ <Icon slot="start-icon" name="markdown" />
116
+ </Button>
116
117
  {/if}
117
118
  </Toolbar>
118
119
  </div>
@@ -1,6 +1,7 @@
1
1
  <script>
2
2
  import { LinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link';
3
3
  import { $getNearestNodeOfType as getNearestNodeOfType } from '@lexical/utils';
4
+ import { generateElementId } from '@sveltia/utils/element';
4
5
  import { isURL } from '@sveltia/utils/string';
5
6
  import {
6
7
  COMMAND_PRIORITY_NORMAL,
@@ -12,12 +13,14 @@
12
13
  } from 'lexical';
13
14
  import { getContext, onMount } from 'svelte';
14
15
  import { _ } from 'svelte-i18n';
15
- import Button from '../../button/button.svelte';
16
- import Dialog from '../../dialog/dialog.svelte';
17
- import Icon from '../../icon/icon.svelte';
18
- import { availableButtons } from '..';
19
- import TextInput from '../../text-field/text-input.svelte';
20
16
  import { isMac, matchShortcuts } from '../../../services/events';
17
+ import TextInput from '../../text-field/text-input.svelte';
18
+ import { availableButtons } from '..';
19
+ import Icon from '../../icon/icon.svelte';
20
+ import Dialog from '../../dialog/dialog.svelte';
21
+ import Button from '../../button/button.svelte';
22
+
23
+ const id = generateElementId('insert-link');
21
24
 
22
25
  /**
23
26
  * Button type.
@@ -186,34 +189,30 @@
186
189
  >
187
190
  {#if !hasAnchor}
188
191
  <div role="none">
189
- <!-- svelte-ignore a11y-label-has-associated-control -->
190
- <label>
191
- {$_('_sui.text_editor.text')}<br />
192
- <TextInput
193
- autofocus
194
- bind:value={anchorText}
195
- flex
196
- on:keydown={(event) => {
197
- onInputKeyDown(event);
198
- }}
199
- />
200
- </label>
201
- </div>
202
- {/if}
203
- <div role="none">
204
- <!-- svelte-ignore a11y-label-has-associated-control -->
205
- <label>
206
- {$_('_sui.text_editor.url')}<br />
192
+ <label for="{id}-text">{$_('_sui.text_editor.text')}</label>
207
193
  <TextInput
208
- autofocus={hasAnchor || undefined}
209
- bind:value={anchorURL}
194
+ id="{id}-text"
195
+ autofocus
196
+ bind:value={anchorText}
210
197
  flex
211
- aria-label="URL"
212
198
  on:keydown={(event) => {
213
199
  onInputKeyDown(event);
214
200
  }}
215
201
  />
216
- </label>
202
+ </div>
203
+ {/if}
204
+ <div role="none">
205
+ <label for="{id}-url">{$_('_sui.text_editor.url')}</label>
206
+ <TextInput
207
+ id="{id}-url"
208
+ autofocus={hasAnchor || undefined}
209
+ bind:value={anchorURL}
210
+ flex
211
+ aria-label="URL"
212
+ on:keydown={(event) => {
213
+ onInputKeyDown(event);
214
+ }}
215
+ />
217
216
  </div>
218
217
  <svelte:fragment slot="footer-extra">
219
218
  {#if dialogMode !== 'create'}
@@ -163,7 +163,7 @@
163
163
  height: var(--sui-button-medium-height);
164
164
  }
165
165
  .search-bar :global(.label) {
166
- --sui-textbox-padding: 0 36px;
166
+ --sui-textbox-singleline-padding: 0 36px;
167
167
  }
168
168
  .search-bar :global(.text-input) {
169
169
  flex: auto;
@@ -84,7 +84,7 @@
84
84
  on:keypress
85
85
  on:input
86
86
  on:change
87
- />
87
+ ></textarea>
88
88
  {#if autoResize}
89
89
  <div class="clone" aria-hidden="true">{value ?? ''}</div>
90
90
  {/if}
@@ -113,7 +113,7 @@ textarea,
113
113
  border-width: var(--sui-textbox-border-width, 1px);
114
114
  border-color: var(--sui-textbox-border-color);
115
115
  border-radius: var(--sui-textbox-border-radius);
116
- padding: 8px;
116
+ padding: var(--sui-textbox-multiline-padding);
117
117
  width: 100%;
118
118
  min-height: 8em;
119
119
  color: var(--sui-textbox-foreground-color);
@@ -153,7 +153,7 @@ input {
153
153
  border-width: var(--sui-textbox-border-width, 1px);
154
154
  border-color: var(--sui-textbox-border-color);
155
155
  border-radius: var(--sui-textbox-border-radius);
156
- padding: var(--sui-textbox-padding, 0 8px);
156
+ padding: var(--sui-textbox-singleline-padding);
157
157
  min-width: 0;
158
158
  height: var(--sui-textbox-height);
159
159
  color: var(--sui-textbox-foreground-color);
@@ -200,7 +200,7 @@ input ~ :global(button) :global(.icon) {
200
200
 
201
201
  .label {
202
202
  position: absolute;
203
- inset: var(--sui-textbox-padding, 0 8px);
203
+ inset: var(--sui-textbox-singleline-padding);
204
204
  z-index: 2;
205
205
  display: flex;
206
206
  align-items: center;
@@ -92,7 +92,7 @@
92
92
  }
93
93
  </script>
94
94
 
95
- <div role="none" class="sui toast-base" bind:this={popoverBase} />
95
+ <div role="none" class="sui toast-base" bind:this={popoverBase}></div>
96
96
 
97
97
  <div class="sui toast {position}" aria-hidden={!show} bind:this={toast} {...$$restProps}>
98
98
  <slot />
@@ -224,8 +224,10 @@
224
224
  --sui-textbox-background-color: hsl(var(--sui-background-color-1-hsl));
225
225
  --sui-textbox-font-family: var(--sui-font-family-default);
226
226
  --sui-textbox-font-size: var(--sui-font-size-default);
227
+ --sui-textbox-singleline-padding: 0 8px;
227
228
  --sui-textbox-singleline-min-width: 240px;
228
229
  --sui-textbox-singleline-line-height: var(--sui-line-height-compact);
230
+ --sui-textbox-multiline-padding: 12px;
229
231
  --sui-textbox-multiline-min-width: 480px;
230
232
  --sui-textbox-multiline-line-height: var(--sui-line-height-comfortable);
231
233
  --sui-tab-height: var(--sui-control-medium-height);
@@ -493,6 +495,11 @@
493
495
  line-height: var(--sui-line-height-comfortable);
494
496
  }
495
497
 
498
+ :global(ul),
499
+ :global(ol) {
500
+ padding-left: 2em;
501
+ }
502
+
496
503
  :global(code),
497
504
  :global(pre) {
498
505
  border-radius: 4px;
@@ -57,8 +57,7 @@ export namespace strings {
57
57
  export const update_link: string;
58
58
  export const text: string;
59
59
  export const url: string;
60
- export const rich_text: string;
61
- export const use_rich_text_mode: string;
60
+ export const edit_in_markdown: string;
62
61
  export const converter_error: string;
63
62
  }
64
63
  }
@@ -56,8 +56,7 @@ export const strings = {
56
56
  update_link: 'Update Link',
57
57
  text: 'Text',
58
58
  url: 'URL',
59
- rich_text: 'Rich Text',
60
- use_rich_text_mode: 'Use Rich Text Mode',
59
+ edit_in_markdown: 'Edit in Markdown',
61
60
  converter_error:
62
61
  'There was an error while enabling rich text mode. Please use the plain text editor instead.',
63
62
  },
@@ -57,8 +57,7 @@ export namespace strings {
57
57
  export const update_link: string;
58
58
  export const text: string;
59
59
  export const url: string;
60
- export const rich_text: string;
61
- export const use_rich_text_mode: string;
60
+ export const edit_in_markdown: string;
62
61
  export const converter_error: string;
63
62
  }
64
63
  }
@@ -56,8 +56,7 @@ export const strings = {
56
56
  update_link: 'リンクを更新',
57
57
  text: 'テキスト',
58
58
  url: 'URL',
59
- rich_text: 'リッチテキスト',
60
- use_rich_text_mode: 'リッチテキストモードを使用',
59
+ edit_in_markdown: 'マークダウンで編集',
61
60
  converter_error:
62
61
  'リッチテキストモードを有効化中に問題が発生しました。代わりにプレーンテキストエディターを使用してください。',
63
62
  },
@@ -102,6 +102,11 @@ li {
102
102
  line-height: var(--sui-line-height-comfortable);
103
103
  }
104
104
 
105
+ ul,
106
+ ol {
107
+ padding-left: 2em;
108
+ }
109
+
105
110
  code,
106
111
  pre {
107
112
  border-radius: 4px;
@@ -219,8 +219,10 @@
219
219
  --sui-textbox-background-color: hsl(var(--sui-background-color-1-hsl));
220
220
  --sui-textbox-font-family: var(--sui-font-family-default);
221
221
  --sui-textbox-font-size: var(--sui-font-size-default);
222
+ --sui-textbox-singleline-padding: 0 8px;
222
223
  --sui-textbox-singleline-min-width: 240px;
223
224
  --sui-textbox-singleline-line-height: var(--sui-line-height-compact);
225
+ --sui-textbox-multiline-padding: 12px;
224
226
  --sui-textbox-multiline-min-width: 480px;
225
227
  --sui-textbox-multiline-line-height: var(--sui-line-height-comfortable);
226
228
  // Tab
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltia/ui",
3
- "version": "0.12.2",
3
+ "version": "0.12.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -48,22 +48,22 @@
48
48
  "eslint-config-prettier": "^9.1.0",
49
49
  "eslint-plugin-import": "^2.29.1",
50
50
  "eslint-plugin-jsdoc": "^48.2.3",
51
- "eslint-plugin-svelte": "^2.37.0",
51
+ "eslint-plugin-svelte": "^2.38.0",
52
52
  "npm-run-all": "^4.1.5",
53
53
  "postcss": "^8.4.38",
54
54
  "postcss-html": "^1.6.0",
55
55
  "prettier": "^3.2.5",
56
56
  "prettier-plugin-svelte": "^3.2.3",
57
57
  "sass": "^1.75.0",
58
- "stylelint": "^16.3.1",
58
+ "stylelint": "^16.4.0",
59
59
  "stylelint-config-recommended-scss": "^14.0.0",
60
60
  "stylelint-scss": "^6.2.1",
61
- "svelte-check": "^3.6.9",
61
+ "svelte-check": "^3.7.0",
62
62
  "svelte-i18n": "^4.0.0",
63
63
  "svelte-preprocess": "^5.1.4",
64
64
  "tslib": "^2.6.2",
65
65
  "vite": "^5.2.10",
66
- "vitest": "^1.5.0"
66
+ "vitest": "^1.5.2"
67
67
  },
68
68
  "exports": {
69
69
  "./package.json": "./package.json",