@sveltia/ui 0.2.4 → 0.3.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 (122) hide show
  1. package/package/components/{core → button}/button.svelte +2 -1
  2. package/package/components/{core → button}/button.svelte.d.ts +6 -5
  3. package/package/components/{composite → button}/select-button-group.svelte +8 -5
  4. package/package/components/{composite → button}/select-button-group.svelte.d.ts +6 -6
  5. package/package/components/{core → button}/select-button.svelte +2 -2
  6. package/package/components/{core → button}/select-button.svelte.d.ts +2 -2
  7. package/package/components/{composite → calendar}/calendar.svelte +9 -5
  8. package/package/components/{composite → calendar}/calendar.svelte.d.ts +1 -0
  9. package/package/components/{composite → checkbox}/checkbox-group.svelte +3 -2
  10. package/package/components/{composite → checkbox}/checkbox-group.svelte.d.ts +2 -2
  11. package/package/components/{core → checkbox}/checkbox.svelte +38 -17
  12. package/package/components/{core → checkbox}/checkbox.svelte.d.ts +7 -3
  13. package/package/components/{core → dialog}/dialog.svelte +5 -4
  14. package/package/components/{core → dialog}/dialog.svelte.d.ts +2 -1
  15. package/package/components/{composite → disclosure}/disclosure.svelte +5 -4
  16. package/package/components/{composite → disclosure}/disclosure.svelte.d.ts +2 -1
  17. package/package/components/{core/separator.svelte → divider/divider.svelte} +5 -4
  18. package/package/components/divider/divider.svelte.d.ts +29 -0
  19. package/package/components/{core → divider}/spacer.svelte +4 -0
  20. package/package/components/{core → divider}/spacer.svelte.d.ts +1 -0
  21. package/package/components/{core → drawer}/drawer.svelte +5 -4
  22. package/package/components/{core → drawer}/drawer.svelte.d.ts +2 -1
  23. package/package/components/{core → icon}/icon.svelte +5 -0
  24. package/package/components/{core → icon}/icon.svelte.d.ts +6 -2
  25. package/package/components/listbox/listbox.svelte +74 -0
  26. package/package/components/{composite → listbox}/listbox.svelte.d.ts +3 -1
  27. package/package/components/listbox/option-group.svelte +47 -0
  28. package/package/components/listbox/option-group.svelte.d.ts +38 -0
  29. package/package/components/{core → listbox}/option.svelte +34 -2
  30. package/package/components/{core → listbox}/option.svelte.d.ts +7 -3
  31. package/package/components/{core → menu}/menu-button.svelte +3 -17
  32. package/package/components/{core → menu}/menu-button.svelte.d.ts +4 -1
  33. package/package/components/{core → menu}/menu-item-checkbox.svelte +1 -0
  34. package/package/components/{core → menu}/menu-item-checkbox.svelte.d.ts +4 -1
  35. package/package/components/{composite → menu}/menu-item-group.svelte +5 -1
  36. package/package/components/{composite → menu}/menu-item-group.svelte.d.ts +1 -0
  37. package/package/components/{core → menu}/menu-item-radio.svelte +2 -0
  38. package/package/components/{core → menu}/menu-item-radio.svelte.d.ts +5 -1
  39. package/package/components/{core → menu}/menu-item.svelte +6 -6
  40. package/package/components/{core → menu}/menu-item.svelte.d.ts +4 -1
  41. package/package/components/{composite → menu}/menu.svelte +3 -2
  42. package/package/components/{composite → menu}/menu.svelte.d.ts +2 -1
  43. package/package/components/{composite/radio-button-group.svelte → radio/radio-group.svelte} +15 -10
  44. package/package/components/radio/radio-group.svelte.d.ts +40 -0
  45. package/package/components/{core/radio-button.svelte → radio/radio.svelte} +45 -18
  46. package/package/components/radio/radio.svelte.d.ts +43 -0
  47. package/package/components/{composite → select}/combobox.svelte +7 -6
  48. package/package/components/{composite → select}/combobox.svelte.d.ts +4 -3
  49. package/package/components/{composite → select}/select.svelte +3 -1
  50. package/package/components/{composite → select}/select.svelte.d.ts +7 -3
  51. package/package/components/{core → slider}/slider.svelte +82 -57
  52. package/package/components/{core → slider}/slider.svelte.d.ts +12 -10
  53. package/package/components/{core → switch}/switch.svelte +36 -19
  54. package/package/components/{core → switch}/switch.svelte.d.ts +4 -3
  55. package/package/components/table/table-body.svelte +23 -0
  56. package/package/components/table/table-body.svelte.d.ts +34 -0
  57. package/package/components/table/table-cell.svelte +23 -0
  58. package/package/components/table/table-cell.svelte.d.ts +34 -0
  59. package/package/components/table/table-col-header.svelte +23 -0
  60. package/package/components/table/table-col-header.svelte.d.ts +34 -0
  61. package/package/components/table/table-foot.svelte +23 -0
  62. package/package/components/table/table-foot.svelte.d.ts +34 -0
  63. package/package/components/table/table-head.svelte +23 -0
  64. package/package/components/table/table-head.svelte.d.ts +34 -0
  65. package/package/components/table/table-row-header.svelte +23 -0
  66. package/package/components/table/table-row-header.svelte.d.ts +34 -0
  67. package/package/components/table/table-row.svelte +23 -0
  68. package/package/components/table/table-row.svelte.d.ts +38 -0
  69. package/package/components/table/table.svelte +44 -0
  70. package/package/components/table/table.svelte.d.ts +36 -0
  71. package/package/components/{composite → tabs}/tab-list.svelte +3 -2
  72. package/package/components/{composite → tabs}/tab-list.svelte.d.ts +7 -6
  73. package/package/components/{core → tabs}/tab-panel.svelte +2 -1
  74. package/package/components/{core → tabs}/tab-panel.svelte.d.ts +2 -1
  75. package/package/components/{core → tabs}/tab.svelte +3 -2
  76. package/package/components/{core → tabs}/tab.svelte.d.ts +2 -1
  77. package/package/components/{editor/markdown.svelte → text-field/markdown-editor.svelte} +10 -6
  78. package/package/components/text-field/markdown-editor.svelte.d.ts +26 -0
  79. package/package/components/{core → text-field}/number-input.svelte +22 -12
  80. package/package/components/{core → text-field}/number-input.svelte.d.ts +7 -3
  81. package/package/components/{core → text-field}/password-input.svelte +5 -2
  82. package/package/components/{core → text-field}/password-input.svelte.d.ts +8 -3
  83. package/package/components/{core → text-field}/search-bar.svelte +5 -2
  84. package/package/components/{core → text-field}/search-bar.svelte.d.ts +8 -3
  85. package/package/components/{core → text-field}/text-area.svelte +2 -0
  86. package/package/components/{core → text-field}/text-area.svelte.d.ts +9 -5
  87. package/package/components/{core → text-field}/text-input.svelte +3 -1
  88. package/package/components/{core → text-field}/text-input.svelte.d.ts +11 -7
  89. package/package/components/{core → toolbar}/toolbar.svelte +2 -1
  90. package/package/components/{core → toolbar}/toolbar.svelte.d.ts +3 -2
  91. package/package/components/util/app-shell.svelte +10 -36
  92. package/package/components/util/group.js +305 -0
  93. package/package/components/{core → util}/group.svelte +5 -11
  94. package/package/components/{core → util}/group.svelte.d.ts +4 -2
  95. package/package/components/util/popup.d.ts +30 -0
  96. package/package/components/{helpers → util}/popup.js +36 -26
  97. package/package/components/util/popup.svelte +14 -5
  98. package/package/components/util/{misc.d.ts → util.d.ts} +1 -0
  99. package/package/components/util/{misc.js → util.js} +15 -0
  100. package/package/index.d.ts +46 -41
  101. package/package/index.js +48 -83
  102. package/package/styles/core.scss +5 -34
  103. package/package/styles/variables.scss +5 -4
  104. package/package.json +362 -328
  105. package/package/components/composite/grid.svelte +0 -24
  106. package/package/components/composite/grid.svelte.d.ts +0 -31
  107. package/package/components/composite/listbox.svelte +0 -63
  108. package/package/components/composite/radio-button-group.svelte.d.ts +0 -36
  109. package/package/components/core/grid-cell.svelte +0 -13
  110. package/package/components/core/grid-cell.svelte.d.ts +0 -29
  111. package/package/components/core/radio-button.svelte.d.ts +0 -37
  112. package/package/components/core/row-group.svelte +0 -13
  113. package/package/components/core/row-group.svelte.d.ts +0 -29
  114. package/package/components/core/row.svelte +0 -13
  115. package/package/components/core/row.svelte.d.ts +0 -33
  116. package/package/components/core/separator.svelte.d.ts +0 -26
  117. package/package/components/editor/markdown.svelte.d.ts +0 -25
  118. package/package/components/helpers/group.js +0 -251
  119. package/package/components/helpers/popup.d.ts +0 -30
  120. package/package/components/helpers/util.d.ts +0 -1
  121. package/package/components/helpers/util.js +0 -14
  122. /package/package/components/{helpers → util}/group.d.ts +0 -0
@@ -1,12 +1,14 @@
1
1
  <!--
2
2
  @component
3
+ The equivalent of the HTML `<input type="radio">` element.
4
+ @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio
3
5
  @see https://w3c.github.io/aria/#radio
4
- @see https://w3c.github.io/aria-practices/#radiobutton
6
+ @see https://www.w3.org/WAI/ARIA/apg/patterns/radio/
5
7
  -->
6
8
  <script>
7
- import { getRandomId } from '../helpers/util';
8
- import Button from './button.svelte';
9
- import Icon from './icon.svelte';
9
+ import Button from '../button/button.svelte';
10
+ import Icon from '../icon/icon.svelte';
11
+ import { getRandomId } from '../util/util';
10
12
 
11
13
  /**
12
14
  * CSS class name on the button.
@@ -19,12 +21,18 @@
19
21
  /** @type {string} */
20
22
  export let name = '';
21
23
 
24
+ /** @type {string?} */
25
+ export let label = undefined;
26
+
22
27
  /** @type {string?} */
23
28
  export let value = undefined;
24
29
 
25
30
  /** @type {boolean} */
26
31
  export let selected = false;
27
32
 
33
+ /** @type {boolean} */
34
+ export let disabled = false;
35
+
28
36
  const id = getRandomId('checkbox');
29
37
  /** @type {Button} */
30
38
  let buttonComponent;
@@ -36,7 +44,7 @@
36
44
 
37
45
  <!-- svelte-ignore a11y-click-events-have-key-events -->
38
46
  <span
39
- class="sui radio-button {className}"
47
+ class="sui radio {className}"
40
48
  on:click={(event) => {
41
49
  if (!(/** @type {HTMLElement} */ (event.target).matches('button'))) {
42
50
  buttonComponent.element.click();
@@ -45,23 +53,32 @@
45
53
  >
46
54
  <Button
47
55
  {id}
56
+ {disabled}
57
+ {name}
58
+ {value}
48
59
  role="radio"
49
60
  aria-checked={selected}
50
61
  aria-labelledby="{id}-label"
51
62
  bind:this={buttonComponent}
52
63
  on:click={(event) => {
53
64
  event.preventDefault();
54
- selected = !selected;
65
+ selected = true;
55
66
  }}
56
67
  >
57
68
  <Icon slot="start-icon" name="circle" />
58
69
  </Button>
59
- <label id="{id}-label">
60
- <slot />
61
- </label>
70
+ {#if $$slots.default || label}
71
+ <label id="{id}-label">
72
+ {#if $$slots.default}
73
+ <slot />
74
+ {:else}
75
+ {label}
76
+ {/if}
77
+ </label>
78
+ {/if}
62
79
  </span>
63
80
 
64
- <style>.radio-button {
81
+ <style>.radio {
65
82
  display: inline-flex;
66
83
  align-items: center;
67
84
  gap: 8px;
@@ -70,24 +87,34 @@
70
87
  -webkit-user-select: none;
71
88
  user-select: none;
72
89
  }
73
- .radio-button :global(button) {
90
+ .radio.disabled {
91
+ cursor: default;
92
+ }
93
+ .radio.disabled label {
94
+ color: var(--disabled-foreground-color);
95
+ }
96
+ .radio :global(button) {
74
97
  justify-content: center;
75
98
  overflow: hidden;
76
99
  border-width: 2px;
77
- border-color: var(--control-border-color);
100
+ border-color: var(--checkbox-border-color);
78
101
  border-radius: 24px;
79
- width: 24px;
80
- height: 24px;
102
+ width: 20px;
103
+ height: 20px;
81
104
  color: var(--primary-accent-color-lighter);
82
105
  transition: all 200ms;
83
106
  }
84
- .radio-button :global(button) :global(.icon) {
85
- font-size: var(--font-size--large);
107
+ .radio :global(button) :global(.icon) {
108
+ font-size: 14px;
86
109
  font-variation-settings: "FILL" 1, "wght" 400, "GRAD" 0, "opsz" 48;
87
110
  }
88
- .radio-button :global(button[aria-checked="true"]) {
111
+ .radio :global(button[aria-checked="true"]) {
112
+ border-color: var(--primary-accent-color-lighter);
89
113
  color: var(--primary-accent-color-lighter);
90
114
  }
91
- .radio-button :global(button[aria-checked="false"]) {
115
+ .radio :global(button[aria-checked="false"]) {
92
116
  color: transparent;
117
+ }
118
+ .radio label {
119
+ cursor: inherit;
93
120
  }</style>
@@ -0,0 +1,43 @@
1
+ /** @typedef {typeof __propDef.props} RadioProps */
2
+ /** @typedef {typeof __propDef.events} RadioEvents */
3
+ /** @typedef {typeof __propDef.slots} RadioSlots */
4
+ /**
5
+ * The equivalent of the HTML `<input type="radio">` element.
6
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio
7
+ * @see https://w3c.github.io/aria/#radio
8
+ * @see https://www.w3.org/WAI/ARIA/apg/patterns/radio/
9
+ */
10
+ export default class Radio extends SvelteComponentTyped<{
11
+ label?: string;
12
+ disabled?: boolean;
13
+ name?: string;
14
+ value?: string;
15
+ class?: string;
16
+ selected?: boolean;
17
+ }, {
18
+ [evt: string]: CustomEvent<any>;
19
+ }, {
20
+ default: {};
21
+ }> {
22
+ }
23
+ export type RadioProps = typeof __propDef.props;
24
+ export type RadioEvents = typeof __propDef.events;
25
+ export type RadioSlots = typeof __propDef.slots;
26
+ import { SvelteComponentTyped } from "svelte";
27
+ declare const __propDef: {
28
+ props: {
29
+ label?: string | null;
30
+ disabled?: boolean;
31
+ name?: string;
32
+ value?: string | null;
33
+ class?: string;
34
+ selected?: boolean;
35
+ };
36
+ events: {
37
+ [evt: string]: CustomEvent<any>;
38
+ };
39
+ slots: {
40
+ default: {};
41
+ };
42
+ };
43
+ export {};
@@ -1,18 +1,19 @@
1
1
  <!--
2
2
  @component
3
+ A variant of the `<Select>` widget with an auto-complete text input field.
3
4
  @see https://w3c.github.io/aria/#combobox
4
- @see https://w3c.github.io/aria-practices/#combobox
5
+ @see https://www.w3.org/WAI/ARIA/apg/patterns/combobox/
5
6
  -->
6
7
  <script>
7
8
  import { createEventDispatcher } from 'svelte';
8
9
  import { _ } from 'svelte-i18n';
9
10
  import { writable } from 'svelte/store';
10
- import Button from '../core/button.svelte';
11
- import Icon from '../core/icon.svelte';
12
- import TextInput from '../core/text-input.svelte';
13
- import { getRandomId } from '../helpers/util';
11
+ import Button from '../button/button.svelte';
12
+ import Icon from '../icon/icon.svelte';
13
+ import Listbox from '../listbox/listbox.svelte';
14
+ import TextInput from '../text-field/text-input.svelte';
14
15
  import Popup from '../util/popup.svelte';
15
- import Listbox from './listbox.svelte';
16
+ import { getRandomId } from '../util/util';
16
17
 
17
18
  /**
18
19
  * CSS class name on the button.
@@ -2,16 +2,17 @@
2
2
  /** @typedef {typeof __propDef.events} ComboboxEvents */
3
3
  /** @typedef {typeof __propDef.slots} ComboboxSlots */
4
4
  /**
5
+ * A variant of the `<Select>` widget with an auto-complete text input field.
5
6
  * @see https://w3c.github.io/aria/#combobox
6
- * @see https://w3c.github.io/aria-practices/#combobox
7
+ * @see https://www.w3.org/WAI/ARIA/apg/patterns/combobox/
7
8
  */
8
9
  export default class Combobox extends SvelteComponentTyped<{
9
10
  [x: string]: any;
10
11
  label?: string;
11
12
  position?: PopupPosition;
12
- class?: string;
13
13
  disabled?: boolean;
14
14
  value?: string | number;
15
+ class?: string;
15
16
  readOnly?: boolean;
16
17
  }, {
17
18
  change: CustomEvent<any>;
@@ -30,9 +31,9 @@ declare const __propDef: {
30
31
  [x: string]: any;
31
32
  label?: string;
32
33
  position?: PopupPosition;
33
- class?: string;
34
34
  disabled?: boolean;
35
35
  value?: (string | number | undefined);
36
+ class?: string;
36
37
  readOnly?: boolean;
37
38
  };
38
39
  events: {
@@ -1,6 +1,8 @@
1
1
  <!--
2
2
  @component
3
- A read-only variant of `<Combobox>`.
3
+ A read-only variant of `<Combobox>`. The equivalent of the HTML `<select>` element.
4
+ @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select
5
+ @see https://w3c.github.io/aria/#combobox
4
6
  -->
5
7
  <script>
6
8
  import Combobox from './combobox.svelte';
@@ -1,12 +1,16 @@
1
1
  /** @typedef {typeof __propDef.props} SelectProps */
2
2
  /** @typedef {typeof __propDef.events} SelectEvents */
3
3
  /** @typedef {typeof __propDef.slots} SelectSlots */
4
- /** A read-only variant of `<Combobox>`. */
4
+ /**
5
+ * A read-only variant of `<Combobox>`. The equivalent of the HTML `<select>` element.
6
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select
7
+ * @see https://w3c.github.io/aria/#combobox
8
+ */
5
9
  export default class Select extends SvelteComponentTyped<{
6
10
  [x: string]: any;
7
11
  label?: string;
8
- class?: string;
9
12
  value?: string;
13
+ class?: string;
10
14
  }, {
11
15
  change: CustomEvent<any>;
12
16
  } & {
@@ -23,8 +27,8 @@ declare const __propDef: {
23
27
  props: {
24
28
  [x: string]: any;
25
29
  label?: string;
26
- class?: string;
27
30
  value?: string | null;
31
+ class?: string;
28
32
  };
29
33
  events: {
30
34
  change: CustomEvent<any>;
@@ -1,5 +1,7 @@
1
1
  <!--
2
2
  @component
3
+ The equivalent of the HTML `<input type="range">` element, but it comes with the multi-thumb
4
+ support.
3
5
  @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range
4
6
  @see https://w3c.github.io/aria/#slider
5
7
  @see https://www.w3.org/WAI/ARIA/apg/patterns/slider/
@@ -18,11 +20,14 @@
18
20
 
19
21
  export let value = 0;
20
22
  export let sliderLabel = '';
23
+ /** @type {[number, number]} */
21
24
  export let values = undefined;
22
- export let sliderLabels = [];
25
+ /** @type {[string, string]} */
26
+ export let sliderLabels = undefined;
23
27
  export let min = 0;
24
28
  export let max = 100;
25
29
  export let step = 1;
30
+ /** @type {(string[] | number[])} */
26
31
  export let optionLabels = [];
27
32
 
28
33
  $: multiThumb = !!values;
@@ -40,57 +45,59 @@
40
45
  let targetValueIndex = 0;
41
46
 
42
47
  /**
43
- *
44
- * @param {number} diff
48
+ * Move a thumb with mouse.
49
+ * @param {number} diff Distance from the original X position in pixels.
45
50
  */
46
- const dragSlider = (diff) => {
47
- if (diff >= 0 && diff <= barWidth) {
48
- const fromIndex = positionList.findLastIndex((s) => s <= diff);
49
- const toIndex = positionList.findIndex((s) => diff <= s);
51
+ const moveThumb = (diff) => {
52
+ if (diff < 0) {
53
+ diff = 0;
54
+ } else if (diff > barWidth) {
55
+ diff = barWidth;
56
+ }
50
57
 
51
- const index =
52
- Math.abs(positionList[fromIndex] - diff) < Math.abs(positionList[toIndex] - diff)
53
- ? fromIndex
54
- : toIndex;
58
+ const fromIndex = positionList.findLastIndex((s) => s <= diff);
59
+ const toIndex = positionList.findIndex((s) => diff <= s);
55
60
 
56
- if (
57
- sliderPositions[targetValueIndex] === positionList[index] ||
58
- (multiThumb &&
59
- ((targetValueIndex === 0 && sliderPositions[1] <= positionList[index]) ||
60
- (targetValueIndex === 1 && sliderPositions[0] >= positionList[index])))
61
- ) {
62
- return;
63
- }
61
+ const index =
62
+ Math.abs(positionList[fromIndex] - diff) < Math.abs(positionList[toIndex] - diff)
63
+ ? fromIndex
64
+ : toIndex;
64
65
 
65
- sliderPositions[targetValueIndex] = positionList[index];
66
+ if (
67
+ sliderPositions[targetValueIndex] === positionList[index] ||
68
+ (multiThumb &&
69
+ ((targetValueIndex === 0 && sliderPositions[1] <= positionList[index]) ||
70
+ (targetValueIndex === 1 && sliderPositions[0] >= positionList[index])))
71
+ ) {
72
+ return;
73
+ }
66
74
 
67
- if (multiThumb) {
68
- values[targetValueIndex] = valueList[index];
69
- dispatch('change', { values });
70
- } else {
71
- value = valueList[index];
72
- dispatch('change', { value });
73
- }
75
+ if (multiThumb) {
76
+ values[targetValueIndex] = valueList[index];
77
+ } else {
78
+ value = valueList[index];
74
79
  }
75
80
  };
76
81
 
77
82
  /**
78
- *
83
+ * Handle the `keydown` event fired on the slider.
79
84
  * @param {KeyboardEvent} event `keydown` event.
80
- * @param {number} [valueIndex]
85
+ * @param {number} [valueIndex] Index in the {@link values} array to be used to get/set the value.
81
86
  */
82
87
  const onKeyDown = (event, valueIndex = 0) => {
83
- const { key, shiftKey, altKey, ctrlKey, metaKey } = event;
88
+ const { key, ctrlKey, metaKey, shiftKey, altKey } = event;
89
+ const hasModifier = shiftKey || altKey || ctrlKey || metaKey;
84
90
 
85
- if (shiftKey || altKey || ctrlKey || metaKey) {
91
+ if (hasModifier) {
86
92
  return;
87
93
  }
88
94
 
95
+ const _value = multiThumb ? values[valueIndex] : value;
89
96
  let index = -1;
90
97
 
91
98
  if (['ArrowDown', 'ArrowLeft'].includes(key)) {
92
- if (value > min) {
93
- index = valueList.indexOf(value) - 1;
99
+ if (_value > min) {
100
+ index = valueList.indexOf(_value) - 1;
94
101
  }
95
102
 
96
103
  event.preventDefault();
@@ -98,8 +105,8 @@
98
105
  }
99
106
 
100
107
  if (['ArrowUp', 'ArrowRight'].includes(key)) {
101
- if (value < max) {
102
- index = valueList.indexOf(value) + 1;
108
+ if (_value < max) {
109
+ index = valueList.indexOf(_value) + 1;
103
110
  }
104
111
 
105
112
  event.preventDefault();
@@ -115,22 +122,18 @@
115
122
  return;
116
123
  }
117
124
 
118
- sliderPositions[valueIndex] = positionList[index];
119
-
120
125
  if (multiThumb) {
121
126
  values[valueIndex] = valueList[index];
122
- dispatch('change', { values });
123
127
  } else {
124
128
  value = valueList[index];
125
- dispatch('change', { value });
126
129
  }
127
130
  }
128
131
  };
129
132
 
130
133
  /**
131
- *
134
+ * Handle the `mousedown` event fired on the slider.
132
135
  * @param {MouseEvent} event `mousedown` event.
133
- * @param {number} [valueIndex]
136
+ * @param {number} [valueIndex] Index in the {@link values} array to be used to get/set the value.
134
137
  */
135
138
  const onMouseDown = (event, valueIndex = 0) => {
136
139
  const { clientX, screenX } = event;
@@ -142,22 +145,31 @@
142
145
  };
143
146
 
144
147
  /**
145
- *
148
+ * Handle the `mousemove` event fired anywhere on the page.
146
149
  * @param {MouseEvent} event `mousemove` event.
147
150
  */
148
151
  const onMouseMove = (event) => {
149
152
  if (dragging) {
150
- dragSlider(startX + (event.screenX - startScreenX));
153
+ moveThumb(startX + (event.screenX - startScreenX));
154
+ }
155
+ };
156
+
157
+ /**
158
+ * Handle the `mouseup` event fired anywhere on the page.
159
+ */
160
+ const onMouseUp = () => {
161
+ if (dragging) {
162
+ dragging = false;
151
163
  }
152
164
  };
153
165
 
154
166
  /**
155
- *
167
+ * Handle the `click` event fired on the slider.
156
168
  * @param {MouseEvent} event `click` event.
157
169
  */
158
170
  const onClick = (event) => {
159
171
  if (!multiThumb && !dragging) {
160
- dragSlider(/** @type {any} */ (event).layerX);
172
+ moveThumb(/** @type {any} */ (event).layerX);
161
173
  }
162
174
 
163
175
  if (dragging) {
@@ -165,6 +177,20 @@
165
177
  }
166
178
  };
167
179
 
180
+ /**
181
+ * Update the thumb position and fire the `change` event when the value is changed.
182
+ */
183
+ const onValueChange = () => {
184
+ if (multiThumb) {
185
+ sliderPositions[0] = positionList[valueList.indexOf(values[0])];
186
+ sliderPositions[1] = positionList[valueList.indexOf(values[1])];
187
+ dispatch('change', { values });
188
+ } else {
189
+ sliderPositions[0] = positionList[valueList.indexOf(value)];
190
+ dispatch('change', { value });
191
+ }
192
+ };
193
+
168
194
  onMount(() => {
169
195
  barWidth = base.clientWidth;
170
196
 
@@ -174,17 +200,16 @@
174
200
  valueList = new Array(stepCount).fill(0).map((_, index) => index * step + min, 10);
175
201
  positionList = new Array(stepCount).fill(0).map((_, index) => index * stepWidth);
176
202
 
177
- if (multiThumb) {
178
- sliderPositions[0] = positionList[valueList.indexOf(values[0])];
179
- sliderPositions[1] = positionList[valueList.indexOf(values[1])];
180
- } else {
181
- sliderPositions[0] = positionList[valueList.indexOf(value)];
182
- }
203
+ onValueChange();
183
204
  });
205
+
206
+ // @ts-ignore Arguments are triggers
207
+ $: onValueChange(value, values);
184
208
  </script>
185
209
 
186
210
  <svelte:body
187
211
  on:mousemove={onMouseMove}
212
+ on:mouseup={onMouseUp}
188
213
  on:click={() => {
189
214
  dragging = false;
190
215
  }}
@@ -206,12 +231,12 @@
206
231
  style:width="{multiThumb ? sliderPositions[1] - sliderPositions[0] : sliderPositions[0]}px"
207
232
  />
208
233
  <div
209
- role="slider"
210
234
  tabindex="0"
211
- aria-label={multiThumb ? sliderLabels[0] || '' : sliderLabel}
235
+ role="slider"
236
+ aria-label={multiThumb ? sliderLabels?.[0] || '' : sliderLabel}
212
237
  aria-valuemin={min}
213
238
  aria-valuemax={max}
214
- aria-valuenow={value}
239
+ aria-valuenow={multiThumb ? values[0] : value}
215
240
  style:left="{sliderPositions[0]}px"
216
241
  on:mousedown={(event) => {
217
242
  onMouseDown(event, 0);
@@ -222,12 +247,12 @@
222
247
  />
223
248
  {#if multiThumb}
224
249
  <div
225
- role="slider"
226
250
  tabindex="0"
227
- aria-label={sliderLabels[1] || ''}
251
+ role="slider"
252
+ aria-label={sliderLabels?.[1] || ''}
228
253
  aria-valuemin={min}
229
254
  aria-valuemax={max}
230
- aria-valuenow={value}
255
+ aria-valuenow={values[1]}
231
256
  style:left="{sliderPositions[1]}px"
232
257
  on:mousedown={(event) => {
233
258
  onMouseDown(event, 1);
@@ -2,21 +2,23 @@
2
2
  /** @typedef {typeof __propDef.events} SliderEvents */
3
3
  /** @typedef {typeof __propDef.slots} SliderSlots */
4
4
  /**
5
+ * The equivalent of the HTML `<input type="range">` element, but it comes with the multi-thumb
6
+ * support.
5
7
  * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range
6
8
  * @see https://w3c.github.io/aria/#slider
7
9
  * @see https://www.w3.org/WAI/ARIA/apg/patterns/slider/
8
10
  * @see https://www.w3.org/WAI/ARIA/apg/patterns/slider-multithumb/
9
11
  */
10
12
  export default class Slider extends SvelteComponentTyped<{
11
- class?: string;
12
13
  value?: number;
14
+ class?: string;
15
+ sliderLabel?: string;
16
+ values?: [number, number];
17
+ sliderLabels?: [string, string];
13
18
  min?: number;
14
19
  max?: number;
15
20
  step?: number;
16
- sliderLabel?: string;
17
- values?: any;
18
- sliderLabels?: any[];
19
- optionLabels?: any[];
21
+ optionLabels?: string[] | number[];
20
22
  }, {
21
23
  click: MouseEvent;
22
24
  change: CustomEvent<any>;
@@ -30,15 +32,15 @@ export type SliderSlots = typeof __propDef.slots;
30
32
  import { SvelteComponentTyped } from "svelte";
31
33
  declare const __propDef: {
32
34
  props: {
33
- class?: string;
34
35
  value?: number;
36
+ class?: string;
37
+ sliderLabel?: string;
38
+ values?: [number, number];
39
+ sliderLabels?: [string, string];
35
40
  min?: number;
36
41
  max?: number;
37
42
  step?: number;
38
- sliderLabel?: string;
39
- values?: any;
40
- sliderLabels?: any[];
41
- optionLabels?: any[];
43
+ optionLabels?: (string[] | number[]);
42
44
  };
43
45
  events: {
44
46
  click: MouseEvent;
@@ -1,7 +1,8 @@
1
1
  <!--
2
2
  @component
3
+ A variant of `<Checkbox>`, looking like a switch that can be often seen on mobile apps.
3
4
  @see https://w3c.github.io/aria/#switch
4
- @see https://w3c.github.io/aria-practices/#switch
5
+ @see https://www.w3.org/WAI/ARIA/apg/patterns/switch/
5
6
  -->
6
7
  <script>
7
8
  /**
@@ -19,49 +20,65 @@
19
20
  export let disabled = false;
20
21
  </script>
21
22
 
22
- <label class="sui switch {className}" class:disabled>
23
- <input type="checkbox" role="switch" bind:checked {disabled} />
23
+ <button
24
+ class="sui switch {className}"
25
+ {disabled}
26
+ role="switch"
27
+ aria-disabled={disabled}
28
+ aria-checked={checked}
29
+ on:click={() => {
30
+ checked = !checked;
31
+ }}
32
+ >
24
33
  <span />
25
34
  {#if label}
26
35
  {label}
27
36
  {:else}
28
37
  <slot />
29
38
  {/if}
30
- </label>
39
+ </button>
31
40
 
32
- <style>label {
41
+ <style>button {
33
42
  display: inline-flex;
34
43
  align-items: center;
35
44
  gap: 8px;
45
+ border-width: 0;
46
+ border-style: solid;
47
+ border-color: transparent;
48
+ padding: 0;
49
+ color: inherit;
50
+ background-color: transparent;
51
+ box-shadow: none;
52
+ font: inherit;
53
+ text-align: left;
36
54
  cursor: pointer;
37
55
  -webkit-user-select: none;
38
56
  user-select: none;
39
57
  }
40
- label.disabled {
58
+ button:disabled {
41
59
  cursor: default;
42
60
  }
43
-
44
- input {
45
- position: absolute;
46
- left: -99999px;
61
+ button:disabled span {
62
+ opacity: 0.4;
47
63
  }
48
- input:focus + span {
49
- background-color: var(--highlight-background-color);
64
+ button:focus-visible {
65
+ outline: 0;
50
66
  }
51
-
52
- input:checked + span {
67
+ button:focus-visible span::before {
68
+ outline-offset: 1px;
69
+ outline-width: 2px;
70
+ outline-style: solid;
71
+ outline-color: var(--primary-accent-color-lighter);
72
+ }
73
+ button[aria-checked=true] span {
53
74
  background-color: var(--primary-accent-color);
54
75
  border-color: transparent;
55
76
  }
56
- input:checked + span::before {
77
+ button[aria-checked=true] span::before {
57
78
  transform: translateX(22px);
58
79
  background-color: var(--primary-accent-color-foreground);
59
80
  }
60
81
 
61
- input:disabled + span {
62
- opacity: 0.4;
63
- }
64
-
65
82
  span {
66
83
  position: relative;
67
84
  width: 42px;