@sveltia/ui 0.6.6 → 0.7.1

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 (97) hide show
  1. package/package/components/button/button.svelte +112 -75
  2. package/package/components/button/button.svelte.d.ts +45 -18
  3. package/package/components/button/select-button-group.svelte +51 -30
  4. package/package/components/button/select-button-group.svelte.d.ts +12 -8
  5. package/package/components/button/select-button.svelte +50 -2
  6. package/package/components/button/select-button.svelte.d.ts +16 -0
  7. package/package/components/calendar/calendar.svelte +5 -3
  8. package/package/components/checkbox/checkbox-group.svelte +28 -10
  9. package/package/components/checkbox/checkbox-group.svelte.d.ts +4 -2
  10. package/package/components/checkbox/checkbox.svelte +114 -71
  11. package/package/components/checkbox/checkbox.svelte.d.ts +13 -7
  12. package/package/components/dialog/dialog.svelte +37 -17
  13. package/package/components/dialog/dialog.svelte.d.ts +0 -4
  14. package/package/components/disclosure/disclosure.svelte +48 -21
  15. package/package/components/disclosure/disclosure.svelte.d.ts +19 -0
  16. package/package/components/divider/divider.svelte +14 -6
  17. package/package/components/divider/divider.svelte.d.ts +3 -1
  18. package/package/components/divider/spacer.svelte +17 -5
  19. package/package/components/divider/spacer.svelte.d.ts +2 -0
  20. package/package/components/drawer/drawer.svelte +13 -6
  21. package/package/components/listbox/listbox.svelte +55 -20
  22. package/package/components/listbox/listbox.svelte.d.ts +12 -13
  23. package/package/components/listbox/option-group.svelte +22 -5
  24. package/package/components/listbox/option-group.svelte.d.ts +4 -0
  25. package/package/components/listbox/option.svelte +41 -14
  26. package/package/components/listbox/option.svelte.d.ts +9 -3
  27. package/package/components/menu/menu-button.svelte +51 -5
  28. package/package/components/menu/menu-button.svelte.d.ts +15 -0
  29. package/package/components/menu/menu-item-checkbox.svelte +41 -2
  30. package/package/components/menu/menu-item-checkbox.svelte.d.ts +14 -0
  31. package/package/components/menu/menu-item-group.svelte +26 -9
  32. package/package/components/menu/menu-item-group.svelte.d.ts +4 -2
  33. package/package/components/menu/menu-item-radio.svelte +47 -3
  34. package/package/components/menu/menu-item-radio.svelte.d.ts +14 -0
  35. package/package/components/menu/menu-item.svelte +48 -21
  36. package/package/components/menu/menu-item.svelte.d.ts +10 -4
  37. package/package/components/menu/menu.svelte +17 -9
  38. package/package/components/menu/menu.svelte.d.ts +8 -8
  39. package/package/components/radio/radio-group.svelte +50 -11
  40. package/package/components/radio/radio-group.svelte.d.ts +13 -5
  41. package/package/components/radio/radio.svelte +76 -45
  42. package/package/components/radio/radio.svelte.d.ts +11 -5
  43. package/package/components/select/combobox.svelte +59 -29
  44. package/package/components/select/combobox.svelte.d.ts +8 -2
  45. package/package/components/select/select.svelte +31 -6
  46. package/package/components/select/select.svelte.d.ts +10 -2
  47. package/package/components/slider/slider.svelte +158 -70
  48. package/package/components/slider/slider.svelte.d.ts +12 -4
  49. package/package/components/switch/switch.svelte +71 -29
  50. package/package/components/switch/switch.svelte.d.ts +11 -3
  51. package/package/components/table/table-row-header.svelte +1 -0
  52. package/package/components/table/table-row.svelte +16 -3
  53. package/package/components/table/table-row.svelte.d.ts +4 -0
  54. package/package/components/table/table.svelte +2 -1
  55. package/package/components/table/table.svelte.d.ts +4 -0
  56. package/package/components/tabs/tab-list.svelte +33 -16
  57. package/package/components/tabs/tab-list.svelte.d.ts +8 -18
  58. package/package/components/tabs/tab-panel.svelte +1 -2
  59. package/package/components/tabs/tab.svelte +25 -3
  60. package/package/components/tabs/tab.svelte.d.ts +10 -0
  61. package/package/components/text-field/markdown-editor.svelte +54 -22
  62. package/package/components/text-field/markdown-editor.svelte.d.ts +11 -1
  63. package/package/components/text-field/number-input.svelte +66 -15
  64. package/package/components/text-field/number-input.svelte.d.ts +13 -5
  65. package/package/components/text-field/password-input.svelte +42 -9
  66. package/package/components/text-field/password-input.svelte.d.ts +11 -1
  67. package/package/components/text-field/search-bar.svelte +67 -17
  68. package/package/components/text-field/search-bar.svelte.d.ts +26 -1
  69. package/package/components/text-field/text-area.svelte +55 -21
  70. package/package/components/text-field/text-area.svelte.d.ts +12 -19
  71. package/package/components/text-field/text-input.svelte +71 -46
  72. package/package/components/text-field/text-input.svelte.d.ts +34 -14
  73. package/package/components/toast/toast.svelte +120 -0
  74. package/package/components/toast/toast.svelte.d.ts +36 -0
  75. package/package/components/toolbar/toolbar.svelte +33 -10
  76. package/package/components/toolbar/toolbar.svelte.d.ts +7 -1
  77. package/package/components/util/app-shell.svelte +133 -69
  78. package/package/components/util/group.svelte +21 -6
  79. package/package/components/util/group.svelte.d.ts +4 -2
  80. package/package/components/util/popup.svelte +63 -11
  81. package/package/components/util/popup.svelte.d.ts +13 -3
  82. package/package/components/util/portal.svelte +1 -1
  83. package/package/components/util/portal.svelte.d.ts +2 -0
  84. package/package/index.d.ts +2 -0
  85. package/package/index.js +4 -3
  86. package/package/{components/util → services}/events.d.ts +1 -1
  87. package/package/{components/util → services}/events.js +3 -2
  88. package/package/services/group.d.ts +1 -0
  89. package/package/{components/util → services}/group.js +17 -15
  90. package/package/{components/util → services}/util.d.ts +1 -1
  91. package/package/{components/util → services}/util.js +2 -2
  92. package/package/styles/core.scss +36 -7
  93. package/package/styles/variables.scss +98 -62
  94. package/package.json +42 -34
  95. package/package/components/util/group.d.ts +0 -1
  96. /package/package/{components/util → services}/popup.d.ts +0 -0
  97. /package/package/{components/util → services}/popup.js +0 -0
@@ -7,21 +7,28 @@
7
7
  <script>
8
8
  import { onMount } from 'svelte';
9
9
  import { writable } from 'svelte/store';
10
- import { activatePopup } from './popup';
11
- import { sleep } from './util';
10
+ import { activatePopup } from '../../services/popup';
11
+ import { sleep } from '../../services/util';
12
12
 
13
- /** @type {HTMLElement?} */
13
+ /**
14
+ * The `class` attribute on the `<button>` element.
15
+ * @type {string}
16
+ */
17
+ let className = '';
18
+ export { className as class };
19
+
20
+ /** @type {HTMLElement | undefined} */
14
21
  export let anchor = undefined;
15
22
 
16
23
  /**
17
24
  * Reference to the popup element.
18
- * @type {HTMLDialogElement?}
25
+ * @type {HTMLDialogElement | undefined}
19
26
  */
20
27
  export let dialog = undefined;
21
28
 
22
29
  /**
23
30
  * Reference to the content element.
24
- * @type {HTMLElement?}
31
+ * @type {HTMLElement | undefined}
25
32
  */
26
33
  export let content = undefined;
27
34
 
@@ -31,10 +38,19 @@
31
38
  */
32
39
  export let position = 'bottom-left';
33
40
 
41
+ /**
42
+ * Whether to show the popup at the center of the screen on mobile/tablet and ignore the defined
43
+ * dropdown `position`.
44
+ * @type {boolean}
45
+ */
46
+ export let touchOptimized = false;
47
+
34
48
  export let open = writable(false);
35
49
 
36
50
  let showDialog = false;
37
51
  let showContent = false;
52
+ let touchEnabled = false;
53
+ /** @type {string | undefined} */
38
54
  let contentType;
39
55
 
40
56
  let style = writable({
@@ -55,7 +71,7 @@
55
71
  *
56
72
  */
57
73
  const openDialog = () => {
58
- (document.querySelector('.sui.app-shell') || document.body).appendChild(dialog);
74
+ (document.querySelector('.sui.app-shell') ?? document.body).appendChild(dialog);
59
75
  showContent = true;
60
76
  dialog.showModal();
61
77
 
@@ -104,13 +120,16 @@
104
120
  }
105
121
  };
106
122
 
107
- // Call the function only when the `$open` prop is changed
108
- // @ts-ignore
109
- $: toggleDialog($open);
123
+ $: {
124
+ void $open;
125
+ toggleDialog();
126
+ }
110
127
 
111
128
  onMount(() => {
112
129
  dialog.remove();
113
130
 
131
+ touchEnabled = window.matchMedia('(pointer: coarse)').matches;
132
+
114
133
  // onUnmount
115
134
  return () => {
116
135
  dialog?.close();
@@ -119,7 +138,14 @@
119
138
  });
120
139
  </script>
121
140
 
122
- <dialog class="sui popup" bind:this={dialog} class:open={showDialog} {...$$restProps}>
141
+ <dialog
142
+ class="sui popup {className}"
143
+ role="none"
144
+ bind:this={dialog}
145
+ class:touch={touchOptimized && touchEnabled}
146
+ class:open={showDialog}
147
+ {...$$restProps}
148
+ >
123
149
  <div
124
150
  bind:this={content}
125
151
  class="content {contentType}"
@@ -135,7 +161,32 @@
135
161
  </div>
136
162
  </dialog>
137
163
 
138
- <style>.popup.open .content {
164
+ <style>.popup.touch {
165
+ display: flex;
166
+ align-items: center;
167
+ justify-content: center;
168
+ background-color: var(--sui-popup-backdrop-color);
169
+ }
170
+ .popup.touch .content {
171
+ position: static;
172
+ border-width: 0 !important;
173
+ border-radius: 4px !important;
174
+ padding: 8px;
175
+ min-width: 320px !important;
176
+ max-width: calc(100vw - 32px) !important;
177
+ max-height: calc(100vh - 32px) !important;
178
+ }
179
+ .popup.touch.open .content {
180
+ transform: scale(100%) !important;
181
+ }
182
+ .popup.touch:not(.open) .content {
183
+ transform: scale(90%);
184
+ }
185
+ .popup.touch.combobox :global(.sui.listbox) {
186
+ gap: 4px;
187
+ padding: 8px 4px !important;
188
+ }
189
+ .popup.open .content {
139
190
  opacity: 1;
140
191
  transform: translateY(2px);
141
192
  transition-duration: 100ms;
@@ -173,4 +224,5 @@
173
224
  .content.menu :global(.sui.menu) {
174
225
  border-width: 0;
175
226
  border-radius: 0;
227
+ background-color: transparent;
176
228
  }</style>
@@ -4,16 +4,21 @@
4
4
  /** Generic popup helper. */
5
5
  export default class Popup extends SvelteComponent<{
6
6
  [x: string]: any;
7
+ class?: string;
7
8
  dialog?: HTMLDialogElement;
8
9
  position?: PopupPosition;
9
10
  anchor?: HTMLElement;
10
11
  content?: HTMLElement;
12
+ touchOptimized?: boolean;
11
13
  open?: import("svelte/store").Writable<boolean>;
12
14
  }, {
13
15
  [evt: string]: CustomEvent<any>;
14
16
  }, {
15
17
  default: {};
16
18
  }> {
19
+ /**accessor*/
20
+ set class(arg: string);
21
+ get class(): string;
17
22
  /**accessor*/
18
23
  set anchor(arg: HTMLElement);
19
24
  get anchor(): HTMLElement;
@@ -27,6 +32,9 @@ export default class Popup extends SvelteComponent<{
27
32
  set position(arg: PopupPosition);
28
33
  get position(): PopupPosition;
29
34
  /**accessor*/
35
+ set touchOptimized(arg: boolean);
36
+ get touchOptimized(): boolean;
37
+ /**accessor*/
30
38
  set open(arg: import("svelte/store").Writable<boolean>);
31
39
  get open(): import("svelte/store").Writable<boolean>;
32
40
  }
@@ -37,10 +45,12 @@ import { SvelteComponent } from "svelte";
37
45
  declare const __propDef: {
38
46
  props: {
39
47
  [x: string]: any;
40
- dialog?: HTMLDialogElement | null;
48
+ class?: string;
49
+ dialog?: HTMLDialogElement | undefined;
41
50
  position?: PopupPosition;
42
- anchor?: HTMLElement | null;
43
- content?: HTMLElement | null;
51
+ anchor?: HTMLElement | undefined;
52
+ content?: HTMLElement | undefined;
53
+ touchOptimized?: boolean;
44
54
  open?: import("svelte/store").Writable<boolean>;
45
55
  };
46
56
  events: {
@@ -25,7 +25,7 @@
25
25
  });
26
26
  </script>
27
27
 
28
- <div class="sui portal {className}" bind:this={ref}>
28
+ <div class="sui portal {className}" {...$$restProps} bind:this={ref}>
29
29
  <slot />
30
30
  </div>
31
31
 
@@ -3,6 +3,7 @@
3
3
  /** @typedef {typeof __propDef.slots} PortalSlots */
4
4
  /** @see https://github.com/sveltejs/svelte/issues/3088 */
5
5
  export default class Portal extends SvelteComponent<{
6
+ [x: string]: any;
6
7
  class?: string;
7
8
  }, {
8
9
  [evt: string]: CustomEvent<any>;
@@ -16,6 +17,7 @@ export type PortalSlots = typeof __propDef.slots;
16
17
  import { SvelteComponent } from "svelte";
17
18
  declare const __propDef: {
18
19
  props: {
20
+ [x: string]: any;
19
21
  class?: string;
20
22
  };
21
23
  events: {
@@ -47,6 +47,8 @@ export { default as PasswordInput } from "./components/text-field/password-input
47
47
  export { default as SearchBar } from "./components/text-field/search-bar.svelte";
48
48
  export { default as TextArea } from "./components/text-field/text-area.svelte";
49
49
  export { default as TextInput } from "./components/text-field/text-input.svelte";
50
+ export { default as Toast } from "./components/toast/toast.svelte";
50
51
  export { default as Toolbar } from "./components/toolbar/toolbar.svelte";
51
52
  export { default as AppShell } from "./components/util/app-shell.svelte";
52
53
  export { default as Group } from "./components/util/group.svelte";
54
+ export * from "./services/util";
package/package/index.js CHANGED
@@ -1,5 +1,3 @@
1
- // @ts-nocheck
2
-
3
1
  import { addMessages, init } from 'svelte-i18n';
4
2
 
5
3
  /**
@@ -18,7 +16,7 @@ export const initLocales = ({ fallbackLocale = 'en', initialLocale = 'en' } = {}
18
16
  const [, locale] = path.match(/([a-zA-Z-]+)\.js/);
19
17
 
20
18
  // Add `_sui` suffix to avoid collision with app localization
21
- addMessages(locale, { _sui: strings });
19
+ addMessages(locale, /** @type {any} */ ({ _sui: strings }));
22
20
  });
23
21
 
24
22
  init({
@@ -74,6 +72,9 @@ export { default as PasswordInput } from './components/text-field/password-input
74
72
  export { default as SearchBar } from './components/text-field/search-bar.svelte';
75
73
  export { default as TextArea } from './components/text-field/text-area.svelte';
76
74
  export { default as TextInput } from './components/text-field/text-input.svelte';
75
+ export { default as Toast } from './components/toast/toast.svelte';
77
76
  export { default as Toolbar } from './components/toolbar/toolbar.svelte';
78
77
  export { default as AppShell } from './components/util/app-shell.svelte';
79
78
  export { default as Group } from './components/util/group.svelte';
79
+
80
+ export * from './services/util';
@@ -1,3 +1,3 @@
1
1
  export function isMac(): boolean;
2
2
  export function matchShortcuts(event: KeyboardEvent, shortcuts: string): boolean;
3
- export function activateKeyShortcuts(element: (HTMLInputElement | HTMLButtonElement), shortcuts: string): import('svelte/action').ActionReturn;
3
+ export function activateKeyShortcuts(element: (HTMLInputElement | HTMLButtonElement), shortcuts?: string): import('svelte/action').ActionReturn;
@@ -48,12 +48,13 @@ export const matchShortcuts = (event, shortcuts) => {
48
48
  /**
49
49
  * Activate keyboard shortcuts.
50
50
  * @param {(HTMLInputElement | HTMLButtonElement)} element Element.
51
- * @param {string} shortcuts Keyboard shortcuts like `A` or `Accel+S` to focus and click the text
51
+ * @param {string} [shortcuts] Keyboard shortcuts like `A` or `Accel+S` to focus and click the text
52
52
  * field or button. Multiple shortcuts can be defined space-separated. The `Accel` modifier will be
53
53
  * replaced with `Ctrl` on Windows/Linux and `Command` on macOS.
54
54
  * @returns {import('svelte/action').ActionReturn} Actions.
55
55
  */
56
- export const activateKeyShortcuts = (element, shortcuts) => {
56
+ export const activateKeyShortcuts = (element, shortcuts = '') => {
57
+ /** @type {string | undefined} */
57
58
  let platformKeyShortcuts;
58
59
 
59
60
  /**
@@ -0,0 +1 @@
1
+ export function activateGroup(parent: HTMLElement): void;
@@ -68,7 +68,7 @@ class Group {
68
68
 
69
69
  this.orientation = this.grid
70
70
  ? 'horizontal'
71
- : this.parent.getAttribute('aria-orientation') || orientation;
71
+ : this.parent.getAttribute('aria-orientation') ?? orientation;
72
72
  this.childRoles = childRoles;
73
73
  this.childSelectedAttr = childSelectedAttr;
74
74
  this.focusChild = focusChild;
@@ -108,13 +108,14 @@ class Group {
108
108
 
109
109
  /** @type {HTMLElement[]} */
110
110
  get allMembers() {
111
- // @ts-ignore
112
- return [...this.parent.querySelectorAll(this.selector)];
111
+ return /** @type {HTMLElement[]} */ ([...this.parent.querySelectorAll(this.selector)]);
113
112
  }
114
113
 
115
114
  /** @type {HTMLElement[]} */
116
115
  get activeMembers() {
117
- return this.allMembers.filter((element) => !element.matches('[aria-disabled="true"]'));
116
+ return this.allMembers.filter(
117
+ (element) => !element.matches('[aria-disabled="true"], [aria-hidden="true"]'),
118
+ );
118
119
  }
119
120
 
120
121
  /**
@@ -157,15 +158,17 @@ class Group {
157
158
  if (controls) {
158
159
  document.getElementById(controls)?.setAttribute('aria-hidden', String(!isTarget));
159
160
  }
161
+
162
+ if (isTarget) {
163
+ element.scrollIntoView({ block: 'nearest', inline: 'nearest', behavior: 'smooth' });
164
+ }
160
165
  });
161
166
 
162
167
  this.parent.dispatchEvent(
163
- new CustomEvent('select', {
168
+ new CustomEvent('change', {
164
169
  detail: {
165
- // @ts-ignore
166
- value: newTarget.value,
167
- // @ts-ignore
168
- name: newTarget.name,
170
+ value: /** @type {any} */ (newTarget).value,
171
+ name: /** @type {any} */ (newTarget).name,
169
172
  },
170
173
  }),
171
174
  );
@@ -205,7 +208,7 @@ class Group {
205
208
 
206
209
  const currentTarget = (() => {
207
210
  if (!this.focusChild) {
208
- return activeMembers.find((member) => member.matches('.focused')) || activeMembers[0];
211
+ return activeMembers.find((member) => member.matches('.focused')) ?? activeMembers[0];
209
212
  }
210
213
 
211
214
  if (target.matches(this.selector)) {
@@ -253,7 +256,7 @@ class Group {
253
256
  newTarget = allMembers[index + 1];
254
257
  }
255
258
 
256
- if (newTarget?.getAttribute('aria-disabled') === 'true') {
259
+ if (newTarget?.matches('[aria-disabled="true"], [aria-hidden="true"]')) {
257
260
  newTarget = undefined;
258
261
  }
259
262
  } else {
@@ -292,14 +295,13 @@ class Group {
292
295
 
293
296
  /**
294
297
  * Activate a new group.
295
- * @param {...any} args Arguments.
298
+ * @param {HTMLElement} parent Parent element.
296
299
  */
297
- export const activateGroup = (...args) => {
300
+ export const activateGroup = (parent) => {
298
301
  (async () => {
299
302
  // Wait a bit before the relevant components, including the `aria-controls` target are mounted
300
303
  await sleep(100);
301
304
 
302
- // @ts-ignore
303
- return new Group(...args);
305
+ return new Group(parent);
304
306
  })();
305
307
  };
@@ -1,3 +1,3 @@
1
1
  export function getRandomId(prefix?: string, length?: number): string;
2
2
  export function isObject(input: any): boolean;
3
- export function sleep(ms?: number): Promise<any>;
3
+ export function sleep(ms?: number): Promise<void>;
@@ -4,7 +4,7 @@
4
4
  * @param {number} [length] Number of characters to be used in the ID.
5
5
  * @returns {string} Generated ID.
6
6
  */
7
- export const getRandomId = (prefix = '', length = 7) =>
7
+ export const getRandomId = (prefix = 'e', length = 7) =>
8
8
  [
9
9
  prefix,
10
10
  new Array(length)
@@ -25,7 +25,7 @@ export const isObject = (input) =>
25
25
  * Return a simple `Promise` to resolve in the given time, making it easier to wait for a bit in the
26
26
  * code, particularly while making sequential HTTP requests.
27
27
  * @param {number} [ms] Milliseconds to wait.
28
- * @returns {Promise} Nothing.
28
+ * @returns {Promise<void>} Nothing.
29
29
  */
30
30
  export const sleep = (ms = 1000) =>
31
31
  new Promise((resolve) => {
@@ -14,7 +14,9 @@
14
14
  "opsz" 24;
15
15
  }
16
16
 
17
- * {
17
+ *,
18
+ ::before,
19
+ ::after {
18
20
  overflow-anchor: none;
19
21
  scroll-behavior: smooth;
20
22
  box-sizing: border-box;
@@ -27,13 +29,17 @@
27
29
  vertical-align: top;
28
30
  }
29
31
 
32
+ * {
33
+ -webkit-tap-highlight-color: transparent;
34
+ }
35
+
30
36
  :focus {
31
37
  z-index: 1;
32
38
  outline-width: 0;
33
39
  }
34
40
 
35
41
  :focus-visible {
36
- outline-color: var(--sui-primary-accent-color-lighter);
42
+ outline-color: var(--sui-primary-accent-color-light);
37
43
  }
38
44
 
39
45
  h1,
@@ -51,7 +57,7 @@ strong {
51
57
  }
52
58
 
53
59
  a {
54
- color: var(--sui-primary-accent-color-lighter);
60
+ color: var(--sui-primary-accent-color-text);
55
61
  text-decoration: none;
56
62
  }
57
63
 
@@ -78,10 +84,10 @@ dialog {
78
84
  margin: 0;
79
85
  border: 0;
80
86
  padding: 0;
81
- width: 100%;
82
- max-width: 100%;
83
- height: 100%;
84
- max-height: 100%;
87
+ width: 100vw;
88
+ max-width: 100vw;
89
+ height: 100vh;
90
+ max-height: 100vh;
85
91
  color: var(--sui-primary-foreground-color);
86
92
  background: transparent;
87
93
  -webkit-user-select: none;
@@ -93,3 +99,26 @@ dialog {
93
99
  background: transparent;
94
100
  }
95
101
  }
102
+
103
+ .disabled,
104
+ [aria-disabled="true"],
105
+ [inert] {
106
+ cursor: default;
107
+ pointer-events: none;
108
+ -webkit-user-select: none;
109
+ user-select: none;
110
+ filter: grayscale(1) opacity(0.35);
111
+
112
+ :global(*) {
113
+ filter: grayscale(0) opacity(1); // Maintain the opacity on child nodes
114
+ }
115
+ }
116
+
117
+ .disabled *,
118
+ [aria-disabled="true"] *,
119
+ [inert] * {
120
+ cursor: default;
121
+ pointer-events: none;
122
+ -webkit-user-select: none;
123
+ user-select: none;
124
+ }