@streamscloud/kit 0.82.2 → 0.82.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.
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">import { Spinner } from '../_internal/spinner';
2
2
  import { CONTROL_ICON_SIZE, IconSlot } from '../icon';
3
- const { variant = 'primary', size = 'md', iconScale = 'default', disabled = false, loading = false, fullWidth = false, 'aria-label': ariaLabel, icon, iconPosition = 'leading', children, ...mode } = $props();
4
- const inactive = $derived(disabled || loading);
3
+ const { variant = 'primary', size = 'md', iconScale = 'default', disabled = false, loading = false, fullWidth = false, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedby, icon, iconPosition = 'leading', children, ...mode } = $props();
4
+ const inactive = $derived(disabled !== false || loading);
5
5
  const isIconOnly = $derived(!children && (!!icon || loading));
6
6
  const iconFallbacks = $derived({ size: CONTROL_ICON_SIZE[size], scale: iconScale });
7
7
  const btnClass = $derived([
@@ -63,9 +63,10 @@ const handleClick = (e) => {
63
63
  download={mode.download}
64
64
  role="button"
65
65
  aria-label={ariaLabel}
66
+ aria-describedby={ariaDescribedby}
66
67
  aria-disabled={inactive ? 'true' : undefined}
67
68
  aria-busy={loading ? 'true' : undefined}
68
- tabindex={inactive ? -1 : undefined}
69
+ tabindex={disabled === true ? -1 : undefined}
69
70
  onclick={handleClick}>
70
71
  {@render content()}
71
72
  </a>
@@ -77,9 +78,10 @@ const handleClick = (e) => {
77
78
  <button
78
79
  class={btnClass}
79
80
  type={mode.type}
80
- disabled={disabled}
81
+ disabled={disabled === true}
81
82
  aria-label={ariaLabel}
82
- aria-disabled={loading ? 'true' : undefined}
83
+ aria-describedby={ariaDescribedby}
84
+ aria-disabled={inactive ? 'true' : undefined}
83
85
  aria-busy={loading ? 'true' : undefined}
84
86
  form={mode.form}
85
87
  name={mode.name}
@@ -91,7 +93,7 @@ const handleClick = (e) => {
91
93
 
92
94
  <!--
93
95
  @component
94
- A button with configurable variant, size, loading state, and an anchor mode. Width is intrinsic by default — use `fullWidth` for inline-axis stretch, or override `--sc-kit--button--{min,max,}width` from CSS for fine-grained control. Overflowing labels are truncated with an ellipsis.
96
+ A button with configurable variant, size, loading state, and an anchor mode. `disabled: 'focusable'` looks and acts disabled but stays focusable, so a wrapping `Tooltip` can tell keyboard and screen-reader users why. Width is intrinsic by default — use `fullWidth` for inline-axis stretch, or override `--sc-kit--button--{min,max,}width` from CSS for fine-grained control. Overflowing labels are truncated with an ellipsis.
95
97
 
96
98
  The `icon` slot accepts an SVG source string or a snippet; `iconPosition` ('leading' / 'trailing', default leading) flips which side of the label the icon renders on. Icon-only mode kicks in automatically when `icon` is set without `children` — the button becomes square and requires an `aria-label`.
97
99
 
@@ -8,7 +8,8 @@ type BaseProps = {
8
8
  size?: ButtonSize;
9
9
  /** Bump the icon one step up the scale relative to `size`, leaving height/text unchanged. @default 'default' */
10
10
  iconScale?: 'default' | 'large';
11
- disabled?: boolean;
11
+ /** `'focusable'` keeps the button in the tab order as `aria-disabled`, so a wrapping `Tooltip` can explain why it is disabled. */
12
+ disabled?: boolean | 'focusable';
12
13
  /** Renders a spinner in place of the icon (when leading) and blocks clicks. */
13
14
  loading?: boolean;
14
15
  /** Stretch to fill the parent's inline axis. */
@@ -21,6 +22,7 @@ type BaseProps = {
21
22
  children?: Snippet;
22
23
  /** Required when icon-only (no children). */
23
24
  'aria-label'?: string;
25
+ 'aria-describedby'?: string;
24
26
  };
25
27
  type ButtonModeProps = BaseProps & {
26
28
  /** Native button mode. */
@@ -48,7 +50,7 @@ type PresentationalModeProps = BaseProps & {
48
50
  };
49
51
  type Props = ButtonModeProps | AnchorModeProps | PresentationalModeProps;
50
52
  /**
51
- * A button with configurable variant, size, loading state, and an anchor mode. Width is intrinsic by default — use `fullWidth` for inline-axis stretch, or override `--sc-kit--button--{min,max,}width` from CSS for fine-grained control. Overflowing labels are truncated with an ellipsis.
53
+ * A button with configurable variant, size, loading state, and an anchor mode. `disabled: 'focusable'` looks and acts disabled but stays focusable, so a wrapping `Tooltip` can tell keyboard and screen-reader users why. Width is intrinsic by default — use `fullWidth` for inline-axis stretch, or override `--sc-kit--button--{min,max,}width` from CSS for fine-grained control. Overflowing labels are truncated with an ellipsis.
52
54
  *
53
55
  * The `icon` slot accepts an SVG source string or a snippet; `iconPosition` ('leading' / 'trailing', default leading) flips which side of the label the icon renders on. Icon-only mode kicks in automatically when `icon` is set without `children` — the button becomes square and requires an `aria-label`.
54
56
  *
@@ -1,6 +1,6 @@
1
1
  <script lang="ts">import { deriveAccept, FileValidator, openFile } from '../../core/files';
2
2
  import { Button } from '../button';
3
- const { size = 'md', variant = 'secondary', disabled = false, multiple = false, capture, validationRules, icon, iconPosition = 'leading', children, 'aria-label': ariaLabel, on } = $props();
3
+ const { size = 'md', variant = 'secondary', disabled = false, multiple = false, capture, validationRules, icon, iconPosition = 'leading', children, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedby, on } = $props();
4
4
  const accept = $derived(deriveAccept(validationRules ?? []));
5
5
  const handleClick = async () => {
6
6
  const files = await openFile({ multiple, accept, capture });
@@ -22,6 +22,7 @@ const handleClick = async () => {
22
22
  icon={icon}
23
23
  iconPosition={iconPosition}
24
24
  aria-label={ariaLabel}
25
+ aria-describedby={ariaDescribedby}
25
26
  on={{ click: handleClick }}>
26
27
  {#if children}{@render children()}{/if}
27
28
  </Button>
@@ -7,7 +7,8 @@ type Props = {
7
7
  size?: ButtonSize;
8
8
  /** @default 'secondary' */
9
9
  variant?: ButtonVariant;
10
- disabled?: boolean;
10
+ /** `'focusable'` keeps the button in the tab order as `aria-disabled`, so a wrapping `Tooltip` can explain why it is disabled. */
11
+ disabled?: boolean | 'focusable';
11
12
  multiple?: boolean;
12
13
  /** Optional file-picker capture hint (mobile camera / mic). */
13
14
  capture?: 'user' | 'environment';
@@ -26,6 +27,7 @@ type Props = {
26
27
  children?: Snippet;
27
28
  /** Required when no `children` (icon-only mode). */
28
29
  'aria-label'?: string;
30
+ 'aria-describedby'?: string;
29
31
  on?: {
30
32
  /**
31
33
  * Fires after the user picks files. Every result is delivered — consumer reads
@@ -82,13 +82,11 @@ const handleBodyClick = (event) => {
82
82
  <button bind:this={activatorEl} type="button" class="toast__activator" aria-labelledby={contentId}></button>
83
83
  {/if}
84
84
  <div id={contentId} class="toast__body">
85
- <div class="toast__message">
86
- {#if typeof toast.message === 'string'}
87
- {toast.message}
88
- {:else}
89
- {@render toast.message()}
90
- {/if}
91
- </div>
85
+ {#if typeof toast.message === 'string'}
86
+ <div class="toast__message toast__message--text">{toast.message}</div>
87
+ {:else}
88
+ <div class="toast__message">{@render toast.message()}</div>
89
+ {/if}
92
90
  {#if toast.description}
93
91
  <div class="toast__description">{toast.description}</div>
94
92
  {/if}
@@ -178,10 +176,14 @@ const handleBodyClick = (event) => {
178
176
  font-size: var(--sc-kit--font-size--md);
179
177
  line-height: var(--sc-kit--line-height--md);
180
178
  font-weight: var(--sc-kit--font-weight--medium);
181
- text-overflow: ellipsis;
182
- max-width: 100%;
183
- white-space: nowrap;
179
+ }
180
+ .toast__message--text {
181
+ white-space: pre-line;
182
+ word-break: break-word;
183
+ display: -webkit-box;
184
184
  overflow: hidden;
185
+ -webkit-box-orient: vertical;
186
+ -webkit-line-clamp: 3;
185
187
  }
186
188
  .toast__description {
187
189
  font-size: var(--sc-kit--font-size--sm);
@@ -192,7 +194,7 @@ const handleBodyClick = (event) => {
192
194
  display: -webkit-box;
193
195
  overflow: hidden;
194
196
  -webkit-box-orient: vertical;
195
- -webkit-line-clamp: 3;
197
+ -webkit-line-clamp: 4;
196
198
  }
197
199
  .toast__close {
198
200
  --sc-kit--icon--size: 0.75rem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/kit",
3
- "version": "0.82.2",
3
+ "version": "0.82.4",
4
4
  "author": "StreamsCloud",
5
5
  "repository": {
6
6
  "type": "git",